Add a SpaceScreen for listing rooms and subspaces within a space. (#4412)
This commit is contained in:
@@ -583,6 +583,10 @@ extension AccessibilityTests {
|
||||
try await performAccessibilityAudit(named: "SoftLogoutScreen_Previews")
|
||||
}
|
||||
|
||||
func testSpaceHeaderView() async throws {
|
||||
try await performAccessibilityAudit(named: "SpaceHeaderView_Previews")
|
||||
}
|
||||
|
||||
func testSpaceListScreen() async throws {
|
||||
try await performAccessibilityAudit(named: "SpaceListScreen_Previews")
|
||||
}
|
||||
@@ -591,6 +595,10 @@ extension AccessibilityTests {
|
||||
try await performAccessibilityAudit(named: "SpaceRoomCell_Previews")
|
||||
}
|
||||
|
||||
func testSpaceScreen() async throws {
|
||||
try await performAccessibilityAudit(named: "SpaceScreen_Previews")
|
||||
}
|
||||
|
||||
func testSplashScreen() async throws {
|
||||
try await performAccessibilityAudit(named: "SplashScreen_Previews")
|
||||
}
|
||||
|
||||
@@ -205,6 +205,7 @@
|
||||
24B7CD41342C143117ADA768 /* Comparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2B1CC9AA154F4D5435BF60A /* Comparable.swift */; };
|
||||
24BDDD09A90B8BFE3793F3AA /* ClientProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6033779EB37259F27F938937 /* ClientProxyProtocol.swift */; };
|
||||
24C32D7EF94ECF9081638DF6 /* RemotePreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69A05E85E4872C3221C5C287 /* RemotePreference.swift */; };
|
||||
250D2E8309C8CC48796D41D1 /* SpaceScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = C11397904D19CFF0E3689F0E /* SpaceScreenModels.swift */; };
|
||||
25618589E0DE0F1E95FC7B5C /* EmojiProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */; };
|
||||
256D76972BA3254F7CB7F88B /* LocationAnnotation.swift in Sources */ = {isa = PBXBuildFile; fileRef = AAD8234D0E9C9B12BF9F240B /* LocationAnnotation.swift */; };
|
||||
25C4C1100B6EA79F5CC7CBB5 /* AppLockSetupPINScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 989D7380D9C86B3A10D30B13 /* AppLockSetupPINScreenViewModelTests.swift */; };
|
||||
@@ -482,6 +483,7 @@
|
||||
5992EF10AA157EBD97D88910 /* AudioRecorderState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6569593FA36B22259E806A67 /* AudioRecorderState.swift */; };
|
||||
59C41313AED7566C3AC51163 /* RoomSummary.swift in Sources */ = {isa = PBXBuildFile; fileRef = 29A953B6C0C431DBF4DD00B4 /* RoomSummary.swift */; };
|
||||
59F940FCBE6BC343AECEF75E /* ImageCache.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4E2245243369B99216C7D84E /* ImageCache.swift */; };
|
||||
5A58C7C1837D3E5F2C3E8C8C /* SpaceScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8349149254469E24483FB0 /* SpaceScreenViewModel.swift */; };
|
||||
5AA81A4E2D40A32A9E7F71F2 /* ShareExtensionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C3ADF21BE301D0DA48F2A7E /* ShareExtensionView.swift */; };
|
||||
5AC5CD6D893073EE4D9A277E /* ShareExtensionViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3D27299A36536DBF91AE8FA6 /* ShareExtensionViewController.swift */; };
|
||||
5AE6404C4FD4848ACCFF9EDC /* SecureBackupLogoutConfirmationScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1573D28C8A9FB6399D0EEFB /* SecureBackupLogoutConfirmationScreenCoordinator.swift */; };
|
||||
@@ -526,6 +528,7 @@
|
||||
62C5876C4254C58C2086F0DE /* HomeScreenContent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A3B4B58B79A6FA250B24A1EC /* HomeScreenContent.swift */; };
|
||||
63780F9DA06573E38A471ECA /* GenericCallLinkWidgetDriver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 28C202C1C7E330F124981A31 /* GenericCallLinkWidgetDriver.swift */; };
|
||||
6386EA3C898AD1A4BC1DC8A5 /* TimelineMediaPreviewModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9FD40B92FCF20165658296AD /* TimelineMediaPreviewModifier.swift */; };
|
||||
639A0A27383EC655B0E81E95 /* SpaceScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 459A3921046977CBF4F3C359 /* SpaceScreenViewModelProtocol.swift */; };
|
||||
63CDC201A5980F304F6D0A1C /* WaveformInteractionModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFEE91FB8ABB5F5884B6D940 /* WaveformInteractionModifier.swift */; };
|
||||
63DCEBC1DD555E0D645B9E98 /* MapTilerURLBuilderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = F1033290D99D5BBA1AF3560A /* MapTilerURLBuilderProtocol.swift */; };
|
||||
63E46D18B91D08E15FC04125 /* ExpiringTaskRunner.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B25F959A434BB9923A3223F /* ExpiringTaskRunner.swift */; };
|
||||
@@ -580,6 +583,7 @@
|
||||
6BAD956B909A6E29F6CC6E7C /* ButtonStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC23C63849452BC86EA2852 /* ButtonStyle.swift */; };
|
||||
6C34237AFB808E38FC8776B9 /* RoomStateEventStringBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D55702474F279D910D2D162 /* RoomStateEventStringBuilder.swift */; };
|
||||
6C98153D60FF9B648C166C27 /* TimelineItemMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 91FFE1F410969ECB23FE9BB2 /* TimelineItemMenu.swift */; };
|
||||
6CAADDC6318E41C7D7AA9526 /* SpaceScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646B50583A2CE6DA67F7739A /* SpaceScreen.swift */; };
|
||||
6CD61FAF03E8986523C2ABB8 /* StartChatScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3005886F00029F058DB62BE /* StartChatScreenCoordinator.swift */; };
|
||||
6DC8E43BA04AC2AC4EB2EB97 /* AnalyticsPromptScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18486B87745B1811E7FBD3D2 /* AnalyticsPromptScreenModels.swift */; };
|
||||
6E03A710799E6C65C0AB36BC /* TargetConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D829FD8958376614504B18 /* TargetConfiguration.swift */; };
|
||||
@@ -608,6 +612,7 @@
|
||||
71B62C48B8079D49F3FBC845 /* ExpiringTaskRunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 84B7A28A6606D58D1E38C55A /* ExpiringTaskRunnerTests.swift */; };
|
||||
71C1347F23868324A4F43940 /* NavigationModule.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A22A05E472533ED3C5A31B3 /* NavigationModule.swift */; };
|
||||
7254FB2EFDD43BC8BB7A1213 /* SecurityAndPrivacyScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = B4AE42C19EDE64B7CB7BE4D0 /* SecurityAndPrivacyScreen.swift */; };
|
||||
72D2298DE695A6797CDA1A2A /* SpaceScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18B223FA339BF53085328DEE /* SpaceScreenViewModelTests.swift */; };
|
||||
733E2B19AB1FDA3B93293A28 /* AppLockSetupPINScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = D3F275432954C8C6B1B7D966 /* AppLockSetupPINScreen.swift */; };
|
||||
738288EAEE235CAC0893AB9E /* ThreadTimelineScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C9ACDD96F36510C1FC0836B /* ThreadTimelineScreenViewModel.swift */; };
|
||||
73F33E9776B7A50B65A031D2 /* AppLockSettingsScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0BA67B3E4EF9D29D14A78CE /* AppLockSettingsScreenViewModelTests.swift */; };
|
||||
@@ -1231,6 +1236,7 @@
|
||||
E9560744F7B0292E20ECE5F2 /* RoomDetailsScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 63E8A1E8EE094F570573B6E8 /* RoomDetailsScreenViewModelProtocol.swift */; };
|
||||
E96005321849DBD7C72A28F2 /* UITestsAppCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 46C208DA43CE25D13E670F40 /* UITestsAppCoordinator.swift */; };
|
||||
E9985DCD1B0D026D7E8BF809 /* ServerSelectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6640DB5B9171D163E6742639 /* ServerSelectionTests.swift */; };
|
||||
E9B4742B3D6E103327466513 /* SpaceHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3AAC314A877DBDB6EBE1170 /* SpaceHeaderView.swift */; };
|
||||
E9D2ED1C4186931E3D5FDA4E /* QRCodeLoginScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 718D8767035D37E2DB5CC550 /* QRCodeLoginScreenViewModelProtocol.swift */; };
|
||||
EA01A06EEDFEF4AE7652E5F3 /* NSRegularExpresion.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95BAC0F6C9644336E9567EE6 /* NSRegularExpresion.swift */; };
|
||||
EA2FECCD9E00D9784AC6017D /* PhishingDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = AB07F03461023BC39C730922 /* PhishingDetector.swift */; };
|
||||
@@ -1262,6 +1268,7 @@
|
||||
EEC40663922856C65D1E0DF5 /* KeychainControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */; };
|
||||
EED33AFD9334EFD7398707A6 /* VisualListItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD529C89924EE32CE307F36F /* VisualListItem.swift */; };
|
||||
EF0D0155DD104C7A41A2EB0E /* PlainMentionBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AE78FA0011E07920AE83135 /* PlainMentionBuilder.swift */; };
|
||||
EF1C1A5D212B14FFCE0E7C83 /* SpaceScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 094F6B21835890B470DF540C /* SpaceScreenCoordinator.swift */; };
|
||||
EF47D802A404A53F15D5D4B6 /* JoinRoomScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD7C0A2750998C2D77AD00F /* JoinRoomScreenViewModel.swift */; };
|
||||
EF5009AC03212227131C8AF2 /* RoomNotificationSettingsProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E55B5EA766E89FF1F87C3ACB /* RoomNotificationSettingsProxyProtocol.swift */; };
|
||||
EF79B9EFD094C17FBB4942C2 /* UserPreference.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E6DE144D887A254F4CAF203 /* UserPreference.swift */; };
|
||||
@@ -1495,6 +1502,7 @@
|
||||
0825EAFD47332DD459DE893F /* SessionDirectoriesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionDirectoriesTests.swift; sourceTree = "<group>"; };
|
||||
08283301736A6FE9D558B2CB /* AppLockScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
0833F51229E166BCA141D004 /* RoomRolesAndPermissionsFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomRolesAndPermissionsFlowCoordinator.swift; sourceTree = "<group>"; };
|
||||
094F6B21835890B470DF540C /* SpaceScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
099F2D36C141D845A445B1E6 /* EmojiProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiProviderTests.swift; sourceTree = "<group>"; };
|
||||
0A3E77399BD262D301451BF2 /* RoomDetailsEditScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsEditScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
0A459AE4B6566B2FA99E86B2 /* TimelineItemBubbledStylerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbledStylerView.swift; sourceTree = "<group>"; };
|
||||
@@ -1569,6 +1577,7 @@
|
||||
181CF280BC8E3F335AFCB4B8 /* RemotePreferenceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemotePreferenceTests.swift; sourceTree = "<group>"; };
|
||||
18486B87745B1811E7FBD3D2 /* AnalyticsPromptScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AnalyticsPromptScreenModels.swift; sourceTree = "<group>"; };
|
||||
184CF8C196BE143AE226628D /* DecorationTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecorationTimelineItemProtocol.swift; sourceTree = "<group>"; };
|
||||
18B223FA339BF53085328DEE /* SpaceScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
18F2958E6D247AE2516BEEE8 /* ClientProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClientProxy.swift; sourceTree = "<group>"; };
|
||||
190EC7285D3CFEF0D3011BCF /* GeoURI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeoURI.swift; sourceTree = "<group>"; };
|
||||
196004E7695FBA292A7944AF /* ScreenTrackerViewModifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenTrackerViewModifier.swift; sourceTree = "<group>"; };
|
||||
@@ -1814,6 +1823,7 @@
|
||||
4525E8C0FBDD27D1ACE90952 /* SecureBackupKeyBackupScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupKeyBackupScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
4549FCB53F43DB0B278374BC /* TemplateScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TemplateScreen.swift; sourceTree = "<group>"; };
|
||||
4552D3466B1453F287223ADA /* SwipeRightAction.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwipeRightAction.swift; sourceTree = "<group>"; };
|
||||
459A3921046977CBF4F3C359 /* SpaceScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
45A4B934BA41D6C255900265 /* preview_video.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = preview_video.jpg; sourceTree = "<group>"; };
|
||||
45CDF9A107BFE6C79B58D6B5 /* RoomMembersListScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
45D8149FDDA0315CDC553B4B /* UserNotificationCenterProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationCenterProtocol.swift; sourceTree = "<group>"; };
|
||||
@@ -1953,6 +1963,7 @@
|
||||
638A81B97D51591D0FCFA598 /* InteractiveQuickLook.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InteractiveQuickLook.swift; sourceTree = "<group>"; };
|
||||
63E8A1E8EE094F570573B6E8 /* RoomDetailsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomDetailsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
645E027C112740573D27765C /* SecureBackupRecoveryKeyScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupRecoveryKeyScreenModels.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
64F49FB9EE2913234F06CE68 /* MediaPickerScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaPickerScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
@@ -2418,6 +2429,7 @@
|
||||
BEE365C5A4E90ACBE398EFFE /* pt */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pt; path = pt.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||
BEF5FE93A06F563B477F024A /* RoomAvatarImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomAvatarImage.swift; sourceTree = "<group>"; };
|
||||
BF34A2FD6797535C95AC918D /* PlaceholderScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaceholderScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
BF8349149254469E24483FB0 /* SpaceScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
BFA9EA59D5C0DA1BFC7B3621 /* QRCodeLoginScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeLoginScreen.swift; sourceTree = "<group>"; };
|
||||
BFBF273BC2BFB9F3EEFA988B /* CollapsibleRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollapsibleRoomTimelineView.swift; sourceTree = "<group>"; };
|
||||
BFDCAC6CAAD65A2C24EA9C4B /* Dictionary.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Dictionary.swift; sourceTree = "<group>"; };
|
||||
@@ -2430,6 +2442,7 @@
|
||||
C0900BBF0A5D5D775E917C70 /* EventBasedMessageTimelineItemProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EventBasedMessageTimelineItemProtocol.swift; sourceTree = "<group>"; };
|
||||
C0FEA560929DD73FFEF8C3DF /* HomeScreenEmptyStateView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreenEmptyStateView.swift; sourceTree = "<group>"; };
|
||||
C0FF08D0BD7D0B4B6877AB7D /* SecureBackupRecoveryKeyScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupRecoveryKeyScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
C11397904D19CFF0E3689F0E /* SpaceScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceScreenModels.swift; sourceTree = "<group>"; };
|
||||
C142248014E08E885E323E56 /* Avatars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Avatars.swift; sourceTree = "<group>"; };
|
||||
C14D83B2B7CD5501A0089EFC /* LayoutDirection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LayoutDirection.swift; sourceTree = "<group>"; };
|
||||
C1511766C534367700C8DD75 /* RoomNotificationModeProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomNotificationModeProxy.swift; sourceTree = "<group>"; };
|
||||
@@ -2687,6 +2700,7 @@
|
||||
F36C0A6D59717193F49EA986 /* UserSessionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSessionTests.swift; sourceTree = "<group>"; };
|
||||
F37FA1A5D55633E1942B153B /* CallScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
F3A1AB5A84D843B6AC8D5F1E /* AuthenticationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationService.swift; sourceTree = "<group>"; };
|
||||
F3AAC314A877DBDB6EBE1170 /* SpaceHeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SpaceHeaderView.swift; sourceTree = "<group>"; };
|
||||
F3C7252B3461D06175D975A4 /* et */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = et; path = et.lproj/SAS.strings; sourceTree = "<group>"; };
|
||||
F3D94852AD5BB376CBCC3544 /* RoomPreviewProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomPreviewProxy.swift; sourceTree = "<group>"; };
|
||||
F3EAE3E9D5EF4A6D5D9C6CFD /* EmojiPickerScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiPickerScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
@@ -4531,6 +4545,7 @@
|
||||
9FE8A35B4AD5256F4B562274 /* SettingsScreenViewModelTests.swift */,
|
||||
AC43313F21511C853D34544E /* SoftLogoutScreenViewModelTests.swift */,
|
||||
5557DDA438841AF5DC003D0B /* SpaceListScreenViewModelTests.swift */,
|
||||
18B223FA339BF53085328DEE /* SpaceScreenViewModelTests.swift */,
|
||||
6DF438EAFC732D2D95D34BF6 /* StartChatViewModelTests.swift */,
|
||||
C833673B334A0651AB46F30B /* StaticLocationScreenViewModelTests.swift */,
|
||||
2CEBCB9676FCD1D0F13188DD /* StringTests.swift */,
|
||||
@@ -5576,6 +5591,7 @@
|
||||
BDDD421CD80AD0BCBA035076 /* Common */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
F3AAC314A877DBDB6EBE1170 /* SpaceHeaderView.swift */,
|
||||
A0E7B059E84E7E374D3322A2 /* SpaceRoomCell.swift */,
|
||||
);
|
||||
path = Common;
|
||||
@@ -5696,6 +5712,11 @@
|
||||
C360FCF7418FE3593D5A0CBF /* SpaceScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
094F6B21835890B470DF540C /* SpaceScreenCoordinator.swift */,
|
||||
C11397904D19CFF0E3689F0E /* SpaceScreenModels.swift */,
|
||||
BF8349149254469E24483FB0 /* SpaceScreenViewModel.swift */,
|
||||
459A3921046977CBF4F3C359 /* SpaceScreenViewModelProtocol.swift */,
|
||||
FA1D480A302295CFC3582543 /* View */,
|
||||
);
|
||||
path = SpaceScreen;
|
||||
sourceTree = "<group>";
|
||||
@@ -6302,6 +6323,14 @@
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FA1D480A302295CFC3582543 /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
646B50583A2CE6DA67F7739A /* SpaceScreen.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
FB039572AA54E0690B4051AD /* Common */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -7228,6 +7257,7 @@
|
||||
494970EA811FE4D93AC68482 /* SettingsScreenViewModelTests.swift in Sources */,
|
||||
C797C0B4CF45C66CD1921252 /* SoftLogoutScreenViewModelTests.swift in Sources */,
|
||||
920DC020F18ABC88175114D3 /* SpaceListScreenViewModelTests.swift in Sources */,
|
||||
72D2298DE695A6797CDA1A2A /* SpaceScreenViewModelTests.swift in Sources */,
|
||||
6189B4ABD535CE526FA1107B /* StartChatViewModelTests.swift in Sources */,
|
||||
9BB91CABB10D8FE90C491BCD /* StaticLocationScreenViewModelTests.swift in Sources */,
|
||||
1FEC0A4EC6E6DF693C16B32A /* StringTests.swift in Sources */,
|
||||
@@ -8071,6 +8101,7 @@
|
||||
F37629BAA5E8F50AAF2A131D /* SoftLogoutScreenViewModel.swift in Sources */,
|
||||
CF4044A8EED5C41BC0ED6ABE /* SoftLogoutScreenViewModelProtocol.swift in Sources */,
|
||||
8D9A97E32C6C03B884CBD85A /* SpaceExplorerFlowCoordinator.swift in Sources */,
|
||||
E9B4742B3D6E103327466513 /* SpaceHeaderView.swift in Sources */,
|
||||
306ADA9D91EE5F0A30B5E500 /* SpaceListScreen.swift in Sources */,
|
||||
C586E1B286BCD8A774DA16B8 /* SpaceListScreenCoordinator.swift in Sources */,
|
||||
F38186A943D078D30BFB90DE /* SpaceListScreenModels.swift in Sources */,
|
||||
@@ -8081,6 +8112,11 @@
|
||||
2EAA1B35D9CA24F090F48792 /* SpaceRoomListProxyProtocol.swift in Sources */,
|
||||
C73CF79A578133D3AB7FB83D /* SpaceRoomProxyMock.swift in Sources */,
|
||||
BE8075CA131C5EA3665C9E0D /* SpaceRoomProxyProtocol.swift in Sources */,
|
||||
6CAADDC6318E41C7D7AA9526 /* SpaceScreen.swift in Sources */,
|
||||
EF1C1A5D212B14FFCE0E7C83 /* SpaceScreenCoordinator.swift in Sources */,
|
||||
250D2E8309C8CC48796D41D1 /* SpaceScreenModels.swift in Sources */,
|
||||
5A58C7C1837D3E5F2C3E8C8C /* SpaceScreenViewModel.swift in Sources */,
|
||||
639A0A27383EC655B0E81E95 /* SpaceScreenViewModelProtocol.swift in Sources */,
|
||||
A2091F4B1332D9BF273B09D5 /* SpaceServiceProxyMock.swift in Sources */,
|
||||
DB5200B87C4CE9DF0024AC4E /* SpaceServiceProxyProtocol.swift in Sources */,
|
||||
DF004A5B2EABBD0574D06A04 /* SplashScreenCoordinator.swift in Sources */,
|
||||
|
||||
@@ -278,6 +278,7 @@
|
||||
"common_signing_out" = "Signing out";
|
||||
"common_something_went_wrong" = "Something went wrong";
|
||||
"common_something_went_wrong_message" = "We encountered an issue. Please try again.";
|
||||
"common_space" = "Space";
|
||||
"common_starting_chat" = "Starting chat…";
|
||||
"common_sticker" = "Sticker";
|
||||
"common_success" = "Success";
|
||||
@@ -1075,6 +1076,7 @@
|
||||
"screen_roomlist_filter_favourites_empty_state_title" = "You don’t have favourite chats yet";
|
||||
"screen_roomlist_filter_invites_empty_state_title" = "You don't have any pending invites.";
|
||||
"screen_roomlist_filter_low_priority" = "Low Priority";
|
||||
"screen_roomlist_filter_low_priority_empty_state_title" = "You don’t have any low priority chats yet";
|
||||
"screen_roomlist_filter_mixed_empty_state_subtitle" = "You can deselect filters in order to see your other chats";
|
||||
"screen_roomlist_filter_mixed_empty_state_title" = "You don’t have chats for this selection";
|
||||
"screen_roomlist_filter_people_empty_state_title" = "You don’t have any DMs yet";
|
||||
|
||||
@@ -624,6 +624,8 @@ internal enum L10n {
|
||||
internal static var commonSomethingWentWrong: String { return L10n.tr("Localizable", "common_something_went_wrong") }
|
||||
/// We encountered an issue. Please try again.
|
||||
internal static var commonSomethingWentWrongMessage: String { return L10n.tr("Localizable", "common_something_went_wrong_message") }
|
||||
/// Space
|
||||
internal static var commonSpace: String { return L10n.tr("Localizable", "common_space") }
|
||||
/// Plural format key: "%#@COUNT@"
|
||||
internal static func commonSpaces(_ p1: Int) -> String {
|
||||
return L10n.tr("Localizable", "common_spaces", p1)
|
||||
@@ -2581,6 +2583,8 @@ internal enum L10n {
|
||||
internal static var screenRoomlistFilterInvitesEmptyStateTitle: String { return L10n.tr("Localizable", "screen_roomlist_filter_invites_empty_state_title") }
|
||||
/// Low Priority
|
||||
internal static var screenRoomlistFilterLowPriority: String { return L10n.tr("Localizable", "screen_roomlist_filter_low_priority") }
|
||||
/// You don’t have any low priority chats yet
|
||||
internal static var screenRoomlistFilterLowPriorityEmptyStateTitle: String { return L10n.tr("Localizable", "screen_roomlist_filter_low_priority_empty_state_title") }
|
||||
/// You can deselect filters in order to see your other chats
|
||||
internal static var screenRoomlistFilterMixedEmptyStateSubtitle: String { return L10n.tr("Localizable", "screen_roomlist_filter_mixed_empty_state_subtitle") }
|
||||
/// You don’t have chats for this selection
|
||||
|
||||
@@ -5,19 +5,43 @@
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
extension SpaceRoomListProxyMock {
|
||||
struct Configuration {
|
||||
class Configuration {
|
||||
var spaceRoomProxy: SpaceRoomProxyProtocol
|
||||
var spaceRooms: [SpaceRoomProxyProtocol] = []
|
||||
var initialSpaceRooms: [SpaceRoomProxyProtocol]
|
||||
var paginationStateSubject: CurrentValueSubject<SpaceRoomListProxyPaginationState, Never>
|
||||
var paginationResponses: [[SpaceRoomProxyProtocol]]
|
||||
|
||||
init(spaceRoomProxy: SpaceRoomProxyProtocol,
|
||||
initialSpaceRooms: [SpaceRoomProxyProtocol] = [],
|
||||
paginationStateSubject: CurrentValueSubject<SpaceRoomListProxyPaginationState, Never> = .init(.idle(endReached: true)),
|
||||
paginationResponses: [[SpaceRoomProxyProtocol]] = []) {
|
||||
self.spaceRoomProxy = spaceRoomProxy
|
||||
self.initialSpaceRooms = initialSpaceRooms
|
||||
self.paginationStateSubject = paginationStateSubject
|
||||
self.paginationResponses = paginationResponses
|
||||
}
|
||||
}
|
||||
|
||||
convenience init(_ configuration: Configuration) {
|
||||
self.init()
|
||||
|
||||
let spaceRoomsSubject: CurrentValueSubject<[SpaceRoomProxyProtocol], Never> = .init(configuration.initialSpaceRooms)
|
||||
|
||||
spaceRoom = configuration.spaceRoomProxy
|
||||
spaceRoomsPublisher = .init(configuration.spaceRooms)
|
||||
spaceRoomsPublisher = spaceRoomsSubject.asCurrentValuePublisher()
|
||||
paginationStatePublisher = configuration.paginationStateSubject.asCurrentValuePublisher()
|
||||
|
||||
paginateClosure = {
|
||||
configuration.paginationStateSubject.send(.loading)
|
||||
try? await Task.sleep(for: .milliseconds(100))
|
||||
let newRooms = configuration.paginationResponses.removeFirst()
|
||||
spaceRoomsSubject.send(spaceRoomsSubject.value + newRooms)
|
||||
configuration.paginationStateSubject.send(.idle(endReached: configuration.paginationResponses.isEmpty))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,3 +46,79 @@ extension SpaceRoomProxyMock {
|
||||
state = configuration.state
|
||||
}
|
||||
}
|
||||
|
||||
extension [SpaceRoomProxyProtocol] {
|
||||
static var mockJoinedSpaces: [SpaceRoomProxyMock] {
|
||||
[
|
||||
SpaceRoomProxyMock(.init(id: "space1",
|
||||
name: "The Foundation",
|
||||
isSpace: true,
|
||||
childrenCount: 1,
|
||||
joinedMembersCount: 500,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space2",
|
||||
name: "The Second Foundation",
|
||||
isSpace: true,
|
||||
childrenCount: 1,
|
||||
joinedMembersCount: 100,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space3",
|
||||
name: "The Galactic Empire",
|
||||
isSpace: true,
|
||||
childrenCount: 25000,
|
||||
joinedMembersCount: 1_000_000_000,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space4",
|
||||
name: "The Korellians",
|
||||
isSpace: true,
|
||||
childrenCount: 27,
|
||||
joinedMembersCount: 2_000_000,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space5",
|
||||
name: "The Luminists",
|
||||
isSpace: true,
|
||||
childrenCount: 1,
|
||||
joinedMembersCount: 100_000,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space6",
|
||||
name: "The Anacreons",
|
||||
isSpace: true,
|
||||
childrenCount: 25,
|
||||
joinedMembersCount: 400_000,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space7",
|
||||
name: "The Thespians",
|
||||
isSpace: true,
|
||||
childrenCount: 15,
|
||||
joinedMembersCount: 300_000,
|
||||
state: .joined))
|
||||
]
|
||||
}
|
||||
|
||||
static var mockSpaceList: [SpaceRoomProxyProtocol] {
|
||||
makeSpaceRooms(isSpace: true) + makeSpaceRooms(isSpace: false)
|
||||
}
|
||||
|
||||
private static func makeSpaceRooms(isSpace: Bool) -> [SpaceRoomProxyMock] {
|
||||
let typeName = isSpace ? "Space" : "Room"
|
||||
|
||||
return [
|
||||
SpaceRoomProxyMock(.init(id: "!\(typeName.lowercased())1:matrix.org",
|
||||
name: "Company \(typeName)",
|
||||
isSpace: isSpace)),
|
||||
SpaceRoomProxyMock(.init(id: "!\(typeName.lowercased())2:matrix.org",
|
||||
name: "Public \(typeName)",
|
||||
avatarURL: .mockMXCAvatar,
|
||||
isSpace: isSpace,
|
||||
joinedMembersCount: 78,
|
||||
topic: "Discussion on specific topic goes here.",
|
||||
joinRule: .public)),
|
||||
SpaceRoomProxyMock(.init(id: "!\(typeName.lowercased())3:matrix.org",
|
||||
name: "Joined \(typeName)",
|
||||
isSpace: isSpace,
|
||||
joinedMembersCount: 123,
|
||||
topic: "Discussion on specific topic goes here.",
|
||||
state: .joined))
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,10 @@ extension UserProfileProxy {
|
||||
.init(userID: "@charlie:matrix.org", displayName: "Charlie", avatarURL: nil)
|
||||
}
|
||||
|
||||
static var mockDan: UserProfileProxy {
|
||||
.init(userID: "@dan:matrix.org", displayName: "Dan", avatarURL: .mockMXCUserAvatar)
|
||||
}
|
||||
|
||||
static var mockVerbose: UserProfileProxy {
|
||||
.init(userID: "@charlie:matrix.org", displayName: "Charlie is the best display name", avatarURL: nil)
|
||||
}
|
||||
|
||||
@@ -71,6 +71,7 @@ enum UserAvatarSizeOnScreen {
|
||||
case readReceipt
|
||||
case readReceiptSheet
|
||||
case editUserDetails
|
||||
case spaceHeader
|
||||
case completionSuggestions
|
||||
case blockedUsers
|
||||
case knockingUsersBannerStack
|
||||
@@ -91,6 +92,8 @@ enum UserAvatarSizeOnScreen {
|
||||
return 16
|
||||
case .readReceiptSheet:
|
||||
return 32
|
||||
case .spaceHeader:
|
||||
return 20
|
||||
case .completionSuggestions:
|
||||
return 32
|
||||
case .blockedUsers:
|
||||
|
||||
@@ -153,8 +153,10 @@ enum TestablePreviewsDictionary {
|
||||
"SettingsScreen_Previews" : SettingsScreen_Previews.self,
|
||||
"ShimmerOverlay_Previews" : ShimmerOverlay_Previews.self,
|
||||
"SoftLogoutScreen_Previews" : SoftLogoutScreen_Previews.self,
|
||||
"SpaceHeaderView_Previews" : SpaceHeaderView_Previews.self,
|
||||
"SpaceListScreen_Previews" : SpaceListScreen_Previews.self,
|
||||
"SpaceRoomCell_Previews" : SpaceRoomCell_Previews.self,
|
||||
"SpaceScreen_Previews" : SpaceScreen_Previews.self,
|
||||
"SplashScreen_Previews" : SplashScreen_Previews.self,
|
||||
"StackedAvatarsView_Previews" : StackedAvatarsView_Previews.self,
|
||||
"StartChatScreen_Previews" : StartChatScreen_Previews.self,
|
||||
|
||||
195
ElementX/Sources/Screens/Spaces/Common/SpaceHeaderView.swift
Normal file
195
ElementX/Sources/Screens/Spaces/Common/SpaceHeaderView.swift
Normal file
@@ -0,0 +1,195 @@
|
||||
//
|
||||
// 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 Compound
|
||||
import SwiftUI
|
||||
|
||||
struct SpaceHeaderView: View {
|
||||
let spaceRoomProxy: SpaceRoomProxyProtocol
|
||||
let mediaProvider: MediaProviderProtocol?
|
||||
|
||||
var title: String { spaceRoomProxy.name ?? "" }
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 16) {
|
||||
RoomAvatarImage(avatar: spaceRoomProxy.avatar,
|
||||
avatarSize: .room(on: .spaceHeader),
|
||||
mediaProvider: mediaProvider)
|
||||
|
||||
VStack(spacing: 8) {
|
||||
Text(title)
|
||||
.font(.compound.headingLGBold)
|
||||
.foregroundStyle(.compound.textPrimary)
|
||||
.multilineTextAlignment(.center)
|
||||
|
||||
spaceDetails
|
||||
|
||||
SpaceHeaderMembersView(heroes: spaceRoomProxy.heroes,
|
||||
joinedCount: spaceRoomProxy.joinedMembersCount,
|
||||
mediaProvider: mediaProvider)
|
||||
}
|
||||
|
||||
if let topic = spaceRoomProxy.topic {
|
||||
Text(topic)
|
||||
.font(.compound.bodyMD)
|
||||
.foregroundStyle(.compound.textPrimary)
|
||||
.multilineTextAlignment(.center)
|
||||
.lineLimit(2)
|
||||
}
|
||||
}
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.horizontal, 16)
|
||||
.padding(.top, 32)
|
||||
.padding(.bottom, 24)
|
||||
.overlay(alignment: .bottom) {
|
||||
Rectangle()
|
||||
.fill(Color.compound.borderDisabled)
|
||||
.frame(height: 1 / UIScreen.main.scale)
|
||||
}
|
||||
}
|
||||
|
||||
var spaceDetails: some View {
|
||||
Label {
|
||||
Text(L10n.screenSpaceListDetails(spaceDetailsVisibilityTitle, L10n.commonRooms(spaceRoomProxy.childrenCount)))
|
||||
.font(.compound.bodyLG)
|
||||
.foregroundStyle(.compound.textSecondary)
|
||||
.multilineTextAlignment(.center)
|
||||
} icon: {
|
||||
CompoundIcon(spaceDetailsVisibilityIcon, size: .small, relativeTo: .compound.bodyLG)
|
||||
.foregroundStyle(.compound.iconTertiary)
|
||||
}
|
||||
}
|
||||
|
||||
var spaceDetailsVisibilityTitle: String {
|
||||
switch spaceRoomProxy.joinRule {
|
||||
case .public:
|
||||
L10n.commonPublicSpace
|
||||
case .restricted(let rules), .knockRestricted(let rules):
|
||||
// FIXME: Get this from the rule (falling back to a passed in parent??)
|
||||
"<Parent name> space"
|
||||
case .invite, .knock, .private, .custom, .none:
|
||||
L10n.commonPrivateSpace
|
||||
}
|
||||
}
|
||||
|
||||
var spaceDetailsVisibilityIcon: KeyPath<CompoundIcons, Image> {
|
||||
switch spaceRoomProxy.joinRule {
|
||||
case .public:
|
||||
\.public
|
||||
case .restricted, .knockRestricted:
|
||||
\.space
|
||||
case .invite, .knock, .private, .custom, .none:
|
||||
\.lock
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import MatrixRustSDK
|
||||
|
||||
struct SpaceHeaderMembersView: View {
|
||||
let heroes: [UserProfileProxy]
|
||||
let joinedCount: Int
|
||||
|
||||
let mediaProvider: MediaProviderProtocol?
|
||||
|
||||
var body: some View {
|
||||
if heroes.isEmpty {
|
||||
Label(title: title) {
|
||||
CompoundIcon(\.userProfile, size: .small, relativeTo: .compound.bodyMD)
|
||||
.foregroundStyle(.compound.textSecondary)
|
||||
}
|
||||
.font(.compound.bodyMD)
|
||||
.foregroundStyle(.compound.textSecondary)
|
||||
.labelStyle(.custom(spacing: 4))
|
||||
.padding(.trailing, 8)
|
||||
.background(.compound.bgSubtleSecondary, in: Capsule())
|
||||
} else {
|
||||
Label(title: title) {
|
||||
heroesFacePile
|
||||
}
|
||||
.font(.compound.bodyMD)
|
||||
.foregroundStyle(.compound.textSecondary)
|
||||
.labelStyle(.custom(spacing: 6))
|
||||
}
|
||||
}
|
||||
|
||||
func title() -> Text {
|
||||
Text("\(joinedCount)")
|
||||
}
|
||||
|
||||
var heroesFacePile: some View {
|
||||
HStack(spacing: -8) {
|
||||
ForEach(heroes.prefix(3).reversed()) { hero in
|
||||
LoadableAvatarImage(url: hero.avatarURL,
|
||||
name: hero.displayName,
|
||||
contentID: hero.userID,
|
||||
avatarSize: .user(on: .spaceHeader),
|
||||
mediaProvider: mediaProvider)
|
||||
.mask {
|
||||
Circle()
|
||||
.fill(Color.white)
|
||||
.overlay {
|
||||
if hero != heroes.first {
|
||||
Circle()
|
||||
.inset(by: -2)
|
||||
.fill(Color.black)
|
||||
.offset(x: 12)
|
||||
}
|
||||
}
|
||||
.compositingGroup()
|
||||
.luminanceToAlpha()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SpaceHeaderView_Previews: PreviewProvider, TestablePreview {
|
||||
static let mediaProvider = MediaProviderMock(configuration: .init())
|
||||
|
||||
static let spaces = makeSpaceRooms()
|
||||
|
||||
static var previews: some View {
|
||||
VStack(spacing: 0) {
|
||||
ForEach(spaces, id: \.id) { space in
|
||||
SpaceHeaderView(spaceRoomProxy: space, mediaProvider: mediaProvider)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func makeSpaceRooms() -> [SpaceRoomProxyMock] {
|
||||
[
|
||||
SpaceRoomProxyMock(.init(id: "!space1:matrix.org",
|
||||
name: "Company Space",
|
||||
isSpace: true,
|
||||
childrenCount: 10,
|
||||
joinedMembersCount: 50)),
|
||||
SpaceRoomProxyMock(.init(id: "!space2:matrix.org",
|
||||
name: "Community Space",
|
||||
avatarURL: .mockMXCAvatar,
|
||||
isSpace: true,
|
||||
childrenCount: 20,
|
||||
joinedMembersCount: 78,
|
||||
topic: "Description of the space goes right here.",
|
||||
joinRule: .public)),
|
||||
SpaceRoomProxyMock(.init(id: "!space3:matrix.org",
|
||||
name: "Subspace",
|
||||
isSpace: true,
|
||||
childrenCount: 30,
|
||||
joinedMembersCount: 123,
|
||||
heroes: [.mockDan, .mockBob, .mockCharlie, .mockVerbose],
|
||||
topic: ["Description of the space goes right here.",
|
||||
"Lorem ipsum dolor sit amet consectetur.",
|
||||
"Leo viverra morbi habitant in.",
|
||||
"Sem amet enim habitant nibh augue mauris.",
|
||||
"Interdum mauris ultrices tincidunt proin morbi erat aenean risus nibh.",
|
||||
"Diam amet sit fermentum vulputate faucibus."].joined(separator: " "),
|
||||
joinRule: .knockRestricted(rules: [.roomMembership(roomId: "")])))
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -136,8 +136,7 @@ struct SpaceRoomCellButtonStyle: ButtonStyle {
|
||||
struct SpaceRoomCell_Previews: PreviewProvider, TestablePreview {
|
||||
static let mediaProvider = MediaProviderMock(configuration: .init())
|
||||
|
||||
static let spaces = makeSpaceRooms(isSpace: true)
|
||||
static let rooms = makeSpaceRooms(isSpace: false)
|
||||
static let spaces = [SpaceRoomProxyProtocol].mockSpaceList
|
||||
|
||||
static var previews: some View {
|
||||
VStack(spacing: 0) {
|
||||
@@ -146,34 +145,6 @@ struct SpaceRoomCell_Previews: PreviewProvider, TestablePreview {
|
||||
isSelected: false,
|
||||
mediaProvider: mediaProvider) { _ in }
|
||||
}
|
||||
ForEach(rooms, id: \.id) { room in
|
||||
SpaceRoomCell(spaceRoom: room,
|
||||
isSelected: false,
|
||||
mediaProvider: mediaProvider) { _ in }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func makeSpaceRooms(isSpace: Bool) -> [SpaceRoomProxyMock] {
|
||||
let name = isSpace ? "Space" : "Room"
|
||||
|
||||
return [
|
||||
SpaceRoomProxyMock(.init(id: "!space1:matrix.org",
|
||||
name: "Company \(name)",
|
||||
isSpace: isSpace)),
|
||||
SpaceRoomProxyMock(.init(id: "!space2:matrix.org",
|
||||
name: "Public \(name)",
|
||||
avatarURL: .mockMXCAvatar,
|
||||
isSpace: isSpace,
|
||||
joinedMembersCount: 78,
|
||||
topic: "Discussion on specific topic goes here.",
|
||||
joinRule: .public)),
|
||||
SpaceRoomProxyMock(.init(id: "!space3:matrix.org",
|
||||
name: "Joined \(name)",
|
||||
isSpace: isSpace,
|
||||
joinedMembersCount: 123,
|
||||
topic: "Discussion on specific topic goes here.",
|
||||
state: .joined))
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ class SpaceListScreenViewModel: SpaceListScreenViewModelType, SpaceListScreenVie
|
||||
mediaProvider: userSession.mediaProvider)
|
||||
|
||||
spaceServiceProxy.joinedSpacesPublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.joinedSpaces, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
|
||||
@@ -105,56 +105,9 @@ struct SpaceListScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static func makeViewModel(counterValue: Int = 0) -> SpaceListScreenViewModel {
|
||||
let clientProxy = ClientProxyMock(.init())
|
||||
let userSession = UserSessionMock(.init(clientProxy: clientProxy))
|
||||
let spaceService = SpaceServiceProxyMock(
|
||||
.init(
|
||||
joinedSpaces: [
|
||||
SpaceRoomProxyMock(.init(id: "space1",
|
||||
name: "The Foundation",
|
||||
isSpace: true,
|
||||
childrenCount: 1,
|
||||
joinedMembersCount: 500,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space2",
|
||||
name: "The Second Foundation",
|
||||
isSpace: true,
|
||||
childrenCount: 1,
|
||||
joinedMembersCount: 100,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space3",
|
||||
name: "The Galactic Empire",
|
||||
isSpace: true,
|
||||
childrenCount: 25000,
|
||||
joinedMembersCount: 1_000_000_000,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space4",
|
||||
name: "The Korellians",
|
||||
isSpace: true,
|
||||
childrenCount: 27,
|
||||
joinedMembersCount: 2_000_000,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space5",
|
||||
name: "The Luminists",
|
||||
isSpace: true,
|
||||
childrenCount: 1,
|
||||
joinedMembersCount: 100_000,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space6",
|
||||
name: "The Anacreons",
|
||||
isSpace: true,
|
||||
childrenCount: 25,
|
||||
joinedMembersCount: 400_000,
|
||||
state: .joined)),
|
||||
SpaceRoomProxyMock(.init(id: "space7",
|
||||
name: "The Thespians",
|
||||
isSpace: true,
|
||||
childrenCount: 15,
|
||||
joinedMembersCount: 300_000,
|
||||
state: .joined))
|
||||
]
|
||||
)
|
||||
)
|
||||
let viewModel = SpaceListScreenViewModel(userSession: userSession,
|
||||
spaceServiceProxy: spaceService)
|
||||
let spaceService = SpaceServiceProxyMock(.init(joinedSpaces: .mockJoinedSpaces))
|
||||
|
||||
let viewModel = SpaceListScreenViewModel(userSession: userSession, spaceServiceProxy: spaceService)
|
||||
|
||||
return viewModel
|
||||
}
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
// periphery:ignore:all - this is just a space remove this comment once generating the final file
|
||||
|
||||
import Combine
|
||||
import SwiftUI
|
||||
|
||||
struct SpaceScreenCoordinatorParameters {
|
||||
let spaceRoomListProxy: SpaceRoomListProxyProtocol
|
||||
let mediaProvider: MediaProviderProtocol
|
||||
}
|
||||
|
||||
enum SpaceScreenCoordinatorAction {
|
||||
case selectSpace(SpaceRoomProxyProtocol)
|
||||
}
|
||||
|
||||
final class SpaceScreenCoordinator: CoordinatorProtocol {
|
||||
private let parameters: SpaceScreenCoordinatorParameters
|
||||
private let viewModel: SpaceScreenViewModelProtocol
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
private let actionsSubject: PassthroughSubject<SpaceScreenCoordinatorAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<SpaceScreenCoordinatorAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(parameters: SpaceScreenCoordinatorParameters) {
|
||||
self.parameters = parameters
|
||||
|
||||
viewModel = SpaceScreenViewModel(spaceRoomList: parameters.spaceRoomListProxy, mediaProvider: parameters.mediaProvider)
|
||||
}
|
||||
|
||||
func start() {
|
||||
viewModel.actionsPublisher.sink { [weak self] action in
|
||||
MXLog.info("Coordinator: received view model action: \(action)")
|
||||
|
||||
guard let self else { return }
|
||||
switch action {
|
||||
case .selectSpace(let spaceRoom):
|
||||
actionsSubject.send(.selectSpace(spaceRoom))
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
func toPresentable() -> AnyView {
|
||||
AnyView(SpaceScreen(context: viewModel.context))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum SpaceScreenViewModelAction {
|
||||
case selectSpace(SpaceRoomProxyProtocol)
|
||||
}
|
||||
|
||||
struct SpaceScreenViewState: BindableState {
|
||||
let space: SpaceRoomProxyProtocol
|
||||
|
||||
var isPaginating = false
|
||||
var rooms: [SpaceRoomProxyProtocol]
|
||||
|
||||
var bindings = SpaceScreenViewStateBindings()
|
||||
}
|
||||
|
||||
struct SpaceScreenViewStateBindings { }
|
||||
|
||||
enum SpaceScreenViewAction {
|
||||
case spaceAction(SpaceRoomCell.Action)
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
//
|
||||
// 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 SwiftUI
|
||||
|
||||
typealias SpaceScreenViewModelType = StateStoreViewModelV2<SpaceScreenViewState, SpaceScreenViewAction>
|
||||
|
||||
class SpaceScreenViewModel: SpaceScreenViewModelType, SpaceScreenViewModelProtocol {
|
||||
private let actionsSubject: PassthroughSubject<SpaceScreenViewModelAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<SpaceScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
init(spaceRoomList: SpaceRoomListProxyProtocol, mediaProvider: MediaProviderProtocol) {
|
||||
super.init(initialViewState: SpaceScreenViewState(space: spaceRoomList.spaceRoom,
|
||||
rooms: spaceRoomList.spaceRoomsPublisher.value),
|
||||
mediaProvider: mediaProvider)
|
||||
|
||||
spaceRoomList.spaceRoomsPublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.rooms, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
spaceRoomList.paginationStatePublisher
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink { [weak self] paginationState in
|
||||
switch paginationState {
|
||||
case .idle(let endReached):
|
||||
self?.state.isPaginating = false
|
||||
guard !endReached else { return }
|
||||
Task { await spaceRoomList.paginate() }
|
||||
case .loading:
|
||||
self?.state.isPaginating = true
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: SpaceScreenViewAction) {
|
||||
MXLog.info("View model: received view action: \(viewAction)")
|
||||
|
||||
switch viewAction {
|
||||
case .spaceAction(.select(let spaceRoom)):
|
||||
if spaceRoom.isSpace {
|
||||
actionsSubject.send(.selectSpace(spaceRoom))
|
||||
} else {
|
||||
#warning("Implement joining")
|
||||
}
|
||||
case .spaceAction(.join(let spaceID)):
|
||||
#warning("Implement joining.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// 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
|
||||
|
||||
@MainActor
|
||||
protocol SpaceScreenViewModelProtocol {
|
||||
var actionsPublisher: AnyPublisher<SpaceScreenViewModelAction, Never> { get }
|
||||
var context: SpaceScreenViewModelType.Context { get }
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
//
|
||||
// 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 Compound
|
||||
import SwiftUI
|
||||
|
||||
struct SpaceScreen: View {
|
||||
@Bindable var context: SpaceScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 0) {
|
||||
SpaceHeaderView(spaceRoomProxy: context.viewState.space,
|
||||
mediaProvider: context.mediaProvider)
|
||||
rooms
|
||||
}
|
||||
}
|
||||
.navigationTitle(context.viewState.space.name ?? L10n.commonSpace)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
var rooms: some View {
|
||||
ForEach(context.viewState.rooms, id: \.id) { spaceRoom in
|
||||
SpaceRoomCell(spaceRoom: spaceRoom,
|
||||
isSelected: false,
|
||||
mediaProvider: context.mediaProvider) { action in
|
||||
context.send(viewAction: .spaceAction(action))
|
||||
}
|
||||
}
|
||||
|
||||
if context.viewState.isPaginating {
|
||||
ProgressView()
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct SpaceScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = makeViewModel()
|
||||
|
||||
static var previews: some View {
|
||||
NavigationStack {
|
||||
SpaceScreen(context: viewModel.context)
|
||||
}
|
||||
}
|
||||
|
||||
static func makeViewModel() -> SpaceScreenViewModel {
|
||||
let spaceRoomProxy = SpaceRoomProxyMock(.init(id: "!eng-space:matrix.org",
|
||||
name: "Engineering Team",
|
||||
isSpace: true,
|
||||
childrenCount: 30,
|
||||
joinedMembersCount: 76,
|
||||
heroes: [.mockDan, .mockBob, .mockCharlie, .mockVerbose],
|
||||
topic: "Description of the space goes right here. Lorem ipsum dolor sit amet consectetur. Leo viverra morbi habitant in.",
|
||||
joinRule: .knockRestricted(rules: [.roomMembership(roomId: "")])))
|
||||
let spaceRoomListProxy = SpaceRoomListProxyMock(.init(spaceRoomProxy: spaceRoomProxy,
|
||||
initialSpaceRooms: .mockSpaceList))
|
||||
|
||||
let viewModel = SpaceScreenViewModel(spaceRoomList: spaceRoomListProxy,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||
return viewModel
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import Combine
|
||||
import MatrixRustSDK
|
||||
|
||||
// Temporary until the SDK is updated.
|
||||
enum SpaceRoomListProxyPaginationState {
|
||||
enum SpaceRoomListProxyPaginationState: Equatable {
|
||||
case idle(endReached: Bool)
|
||||
case loading
|
||||
}
|
||||
|
||||
@@ -875,6 +875,12 @@ extension PreviewTests {
|
||||
}
|
||||
}
|
||||
|
||||
func testSpaceHeaderView() async throws {
|
||||
for (index, preview) in SpaceHeaderView_Previews._allPreviews.enumerated() {
|
||||
try await assertSnapshots(matching: preview, step: index)
|
||||
}
|
||||
}
|
||||
|
||||
func testSpaceListScreen() async throws {
|
||||
for (index, preview) in SpaceListScreen_Previews._allPreviews.enumerated() {
|
||||
try await assertSnapshots(matching: preview, step: index)
|
||||
@@ -887,6 +893,12 @@ extension PreviewTests {
|
||||
}
|
||||
}
|
||||
|
||||
func testSpaceScreen() async throws {
|
||||
for (index, preview) in SpaceScreen_Previews._allPreviews.enumerated() {
|
||||
try await assertSnapshots(matching: preview, step: index)
|
||||
}
|
||||
}
|
||||
|
||||
func testSplashScreen() async throws {
|
||||
for (index, preview) in SplashScreen_Previews._allPreviews.enumerated() {
|
||||
try await assertSnapshots(matching: preview, step: index)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d89f971df09420eeb3e2393d12851c50da0896366fd914df6e50f8525b1963bc
|
||||
size 207443
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:80d43f3c8bd54048177b925efe184e6324da54a6c342ed2f5629983a15ec946a
|
||||
size 223500
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:11b7484b43434f0d598cc596e7eaaa9d898d25b3aba4dfe9d05bdc78ea9b8187
|
||||
size 138715
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e5ea7da5f53f16d9d7eba70450c61ef7ec6b93f70148d32b0152ffa6ec211c33
|
||||
size 164457
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:04df1d04edeac89a7321c3e02ed38ee85bfa0946337896a334d89d8c561195ff
|
||||
size 207862
|
||||
oid sha256:5e2b6b775f3a198e0a52991a296bc1ad5756af00b67c3e867eac90313e62513a
|
||||
size 207919
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:9eda888ec16f20eb1f116ce198ef518cdeb2795c8c59b674c0d537cca2acdc97
|
||||
size 237007
|
||||
oid sha256:f95f1258a7467c48a260c4d9968822f7e4a68f07e664c115f9d6a127c1cd2cf6
|
||||
size 237067
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:2cf45ff8958dfa9af8bd456e0d72c94c70d769461394ec028dcbe120ba714957
|
||||
size 149449
|
||||
oid sha256:104cfb4b0707e035efd03a941da0da8a6c01d189e8d1b66ec1ed4ef1e245062c
|
||||
size 149502
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:bf1fd488f8022708dcedfd1f367b3cf688e9153e5505c1657810d6f4959b76a0
|
||||
size 167283
|
||||
oid sha256:b4fb5143447b96770909c6a0daddb237e64a3df4e0a48b7aa0d3a60e18c7e586
|
||||
size 167301
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:8a9a4c1899fffb9c101f87eeeb2413c962f72daddb2bdf10b23c1aa8267a6707
|
||||
size 241775
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:4debd3d362dfdcec1eebbade1159b3cbbafad46e2daacb1511ad2d08c847926e
|
||||
size 279363
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e1cac5e788e68553c910fcf9d10213c025c593763e72e448db359b2a50f06bd0
|
||||
size 186591
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:90a7af41076c73f8fc3671d6402bdc9e93e23ea592c7efa6647242c44541a900
|
||||
size 206847
|
||||
@@ -27,15 +27,19 @@ class SpaceListScreenViewModelTests: XCTestCase {
|
||||
XCTAssertEqual(context.viewState.joinedRoomsCount, 0)
|
||||
}
|
||||
|
||||
func testJoinedSpacesSubscription() {
|
||||
func testJoinedSpacesSubscription() async throws {
|
||||
setupViewModel()
|
||||
|
||||
var deferred = deferFulfillment(context.observe(\.viewState.joinedSpaces)) { $0.count == 0 }
|
||||
joinedSpacesSubject.send([])
|
||||
try await deferred.fulfill()
|
||||
XCTAssertEqual(context.viewState.joinedSpaces.count, 0)
|
||||
|
||||
deferred = deferFulfillment(context.observe(\.viewState.joinedSpaces)) { $0.count == 1 }
|
||||
joinedSpacesSubject.send([
|
||||
SpaceRoomProxyMock(.init(isSpace: true))
|
||||
])
|
||||
try await deferred.fulfill()
|
||||
XCTAssertEqual(context.viewState.joinedSpaces.count, 1)
|
||||
}
|
||||
|
||||
|
||||
117
UnitTests/Sources/SpaceScreenViewModelTests.swift
Normal file
117
UnitTests/Sources/SpaceScreenViewModelTests.swift
Normal file
@@ -0,0 +1,117 @@
|
||||
//
|
||||
// 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 XCTest
|
||||
|
||||
@testable import ElementX
|
||||
|
||||
@MainActor
|
||||
class SpaceScreenViewModelTests: XCTestCase {
|
||||
var spaceRoomListProxy: SpaceRoomListProxyMock!
|
||||
let mockSpaceRooms = [SpaceRoomProxyProtocol].mockSpaceList
|
||||
var paginationStateSubject: CurrentValueSubject<SpaceRoomListProxyPaginationState, Never> = .init(.idle(endReached: true))
|
||||
|
||||
var viewModel: SpaceScreenViewModelProtocol!
|
||||
|
||||
var context: SpaceScreenViewModelType.Context {
|
||||
viewModel.context
|
||||
}
|
||||
|
||||
func testInitialState() {
|
||||
setupViewModel()
|
||||
|
||||
XCTAssertFalse(context.viewState.isPaginating)
|
||||
XCTAssertTrue(context.viewState.rooms.isEmpty)
|
||||
XCTAssertFalse(spaceRoomListProxy.paginateCalled)
|
||||
}
|
||||
|
||||
func testSinglePagination() async throws {
|
||||
// Given a space screen view model for a space with a single paginations worth of children.
|
||||
let response = mockSpaceRooms.prefix(3)
|
||||
setupViewModel(paginationResponses: [Array(response)])
|
||||
|
||||
XCTAssertFalse(context.viewState.isPaginating)
|
||||
XCTAssertTrue(context.viewState.rooms.isEmpty)
|
||||
XCTAssertFalse(spaceRoomListProxy.paginateCalled)
|
||||
XCTAssertFalse(response.isEmpty, "There should be some test rooms.")
|
||||
|
||||
// When the pagination is triggered.
|
||||
var deferred = deferFulfillment(spaceRoomListProxy.paginationStatePublisher) { $0 == .loading }
|
||||
paginationStateSubject.send(.idle(endReached: false)) // Invert the default to allow paginate to be called.
|
||||
try await deferred.fulfill()
|
||||
|
||||
// Then the screen should show a paginating indicator.
|
||||
XCTAssertTrue(context.viewState.isPaginating)
|
||||
XCTAssertEqual(spaceRoomListProxy.paginateCallsCount, 1)
|
||||
|
||||
// When waiting for the pagination to finish.
|
||||
deferred = deferFulfillment(spaceRoomListProxy.paginationStatePublisher) { $0 == .idle(endReached: true) }
|
||||
try await deferred.fulfill()
|
||||
|
||||
// Then no more pagination requests should be made the the space rooms should be populated.
|
||||
XCTAssertFalse(context.viewState.isPaginating)
|
||||
XCTAssertEqual(spaceRoomListProxy.paginateCallsCount, 1)
|
||||
XCTAssertEqual(context.viewState.rooms.map(\.id), response.map(\.id))
|
||||
}
|
||||
|
||||
func testMultiplePaginations() async throws {
|
||||
// Given a space screen view model for a space with two distinct paginations worth of children.
|
||||
let response1 = mockSpaceRooms.prefix(3)
|
||||
let response2 = mockSpaceRooms.suffix(mockSpaceRooms.count - 3)
|
||||
setupViewModel(paginationResponses: [Array(response1), Array(response2)])
|
||||
|
||||
XCTAssertFalse(context.viewState.isPaginating)
|
||||
XCTAssertTrue(context.viewState.rooms.isEmpty)
|
||||
XCTAssertFalse(spaceRoomListProxy.paginateCalled)
|
||||
XCTAssertFalse(response1.isEmpty, "There should be some test rooms.")
|
||||
XCTAssertFalse(response2.isEmpty, "There should be more test rooms.")
|
||||
|
||||
// When the pagination is triggered.
|
||||
let deferredIsPaginating = deferFulfillment(context.observe(\.viewState.isPaginating), transitionValues: [true, false, true, false])
|
||||
let deferredState = deferFulfillment(spaceRoomListProxy.paginationStatePublisher, keyPath: \.self, transitionValues: [.loading,
|
||||
.idle(endReached: false),
|
||||
.loading,
|
||||
.idle(endReached: true)])
|
||||
paginationStateSubject.send(.idle(endReached: false)) // Invert the default to allow paginate to be called.
|
||||
|
||||
// Then the screen should show 2 distinct paginations and finish up with all of the rooms visible.
|
||||
try await deferredIsPaginating.fulfill()
|
||||
try await deferredState.fulfill()
|
||||
|
||||
XCTAssertFalse(context.viewState.isPaginating)
|
||||
XCTAssertEqual(spaceRoomListProxy.paginateCallsCount, 2)
|
||||
XCTAssertEqual(context.viewState.rooms.map(\.id), mockSpaceRooms.map(\.id))
|
||||
}
|
||||
|
||||
func testSelectingSpace() async throws {
|
||||
setupViewModel()
|
||||
|
||||
let selectedSpace = mockSpaceRooms[0]
|
||||
let deferred = deferFulfillment(viewModel.actionsPublisher) { _ in true }
|
||||
viewModel.context.send(viewAction: .spaceAction(.select(selectedSpace)))
|
||||
let action = try await deferred.fulfill()
|
||||
|
||||
switch action {
|
||||
case .selectSpace(let spaceRoomProxy) where spaceRoomProxy.id == selectedSpace.id:
|
||||
break
|
||||
default:
|
||||
XCTFail("The action should select the space.")
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func setupViewModel(paginationResponses: [[SpaceRoomProxyProtocol]] = []) {
|
||||
spaceRoomListProxy = SpaceRoomListProxyMock(.init(spaceRoomProxy: SpaceRoomProxyMock(.init(isSpace: true)),
|
||||
paginationStateSubject: paginationStateSubject,
|
||||
paginationResponses: paginationResponses))
|
||||
|
||||
viewModel = SpaceScreenViewModel(spaceRoomList: spaceRoomListProxy,
|
||||
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user