mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
Space filters (#4977)
* Add support for SpaceFilters on the SpaceServiceProxy level and a screen that renders them * Add aliases to a couple of space room mocks * Update translations and related snapshots * Add a feature flag for space filters * Add a new room summary provider filter that takes a list of room identifiers next to the normal state filters This will take the SpaceFilter's descendants and combine it with the normal filters to reduce the rooms shown * Add a new space filter button to the home screen and link it to the SpaceFiltersScreen and the actual RoomSummaryProvider room list filters * Bump the RustSDK to v26.01.20-2 for SpaceFilter support. * Add a cancellation button to the space filters screen * Make the filter presentation mode liquidy, move the sheet to within the home screen. * Address PR comments
This commit is contained in:
@@ -75,6 +75,14 @@ extension AccessibilityTests {
|
||||
try await performAccessibilityAudit(named: "CallNotificationRoomTimelineView_Previews")
|
||||
}
|
||||
|
||||
func testChatsSpaceFilterCell() async throws {
|
||||
try await performAccessibilityAudit(named: "ChatsSpaceFilterCell_Previews")
|
||||
}
|
||||
|
||||
func testChatsSpaceFiltersScreen() async throws {
|
||||
try await performAccessibilityAudit(named: "ChatsSpaceFiltersScreen_Previews")
|
||||
}
|
||||
|
||||
func testCollapsibleRoomTimelineView() async throws {
|
||||
try await performAccessibilityAudit(named: "CollapsibleRoomTimelineView_Previews")
|
||||
}
|
||||
|
||||
@@ -355,6 +355,7 @@
|
||||
3DA57CA0D609A6B37CA1DC2F /* BugReportService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6DC38E64A5ED3FDB201029A /* BugReportService.swift */; };
|
||||
3DAD62988F072607441CB7A5 /* PollFormScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F8664F1FB95AF3C4202478 /* PollFormScreenCoordinator.swift */; };
|
||||
3DAF325D8AE461F7CDB282BD /* StartChatScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6861FE915C7B5466E6962BBA /* StartChatScreen.swift */; };
|
||||
3E2EE5782688054B3026EF8E /* ChatsSpaceFilterCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 847C893CF354BCBDF1351C13 /* ChatsSpaceFilterCell.swift */; };
|
||||
3E3CC3D17908A3BB9F224CC5 /* DeactivateAccountScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67638D34FB6A9110BE481A6F /* DeactivateAccountScreenCoordinator.swift */; };
|
||||
3E7B65C2C97748D5D65AAA8B /* NotificationPermissionsScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DB2FC2AA9A07EE792DF65CF /* NotificationPermissionsScreenModels.swift */; };
|
||||
3EC5A41F9FB7DD63A4DC6144 /* RoomChangeRolesScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B14B1DE3E2D5D26732C49036 /* RoomChangeRolesScreenViewModel.swift */; };
|
||||
@@ -800,6 +801,7 @@
|
||||
89DF67AECBF9D0EE0DDB7737 /* Tracing.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83B574805B9812C111D6215D /* Tracing.swift */; };
|
||||
89E6426C6097F848C125E65C /* SpacesScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD297970D7A0F8BAF870F010 /* SpacesScreenModels.swift */; };
|
||||
8A0BD60CA4A6004DB06B5403 /* MediaUploadingPreprocessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 669F35C505ACE1110589F875 /* MediaUploadingPreprocessor.swift */; };
|
||||
8A2B2836B1B0CCA3CC140560 /* ChatsSpaceFiltersScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 859F51637DA710BBE7B70D6D /* ChatsSpaceFiltersScreenViewModel.swift */; };
|
||||
8A5064CAC8E5F3B18645621D /* CallNotificationRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = AD6E082B0507FB28F966516A /* CallNotificationRoomTimelineView.swift */; };
|
||||
8A83D715940378B9BA9F739E /* RoomInviterLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7EB58E4E8D6D634C246AD5C2 /* RoomInviterLabel.swift */; };
|
||||
8AA84EF202F2EFC8453A97BD /* SecureBackupRecoveryKeyScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 645E027C112740573D27765C /* SecureBackupRecoveryKeyScreenModels.swift */; };
|
||||
@@ -929,6 +931,7 @@
|
||||
A021827B528F1EDC9101CA58 /* AppCoordinatorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */; };
|
||||
A0601810597769B81C2358AF /* EncryptionResetPasswordScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A2B5274C1D3D2999D643786 /* EncryptionResetPasswordScreenViewModelProtocol.swift */; };
|
||||
A07178337F3C0B208B5A77A8 /* NotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB6ED50FE104992419310EEB /* NotificationHandler.swift */; };
|
||||
A076E0A9338FD2D950C3C4A1 /* ChatsSpaceFiltersScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = E63072981793CCA84EE12798 /* ChatsSpaceFiltersScreenViewModelProtocol.swift */; };
|
||||
A0861B727B273B5B3DD7FBF6 /* KnockRequestsListScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D09227E671DB30795C43FFFD /* KnockRequestsListScreenViewModel.swift */; };
|
||||
A0868BDE84D2140A885BE3C9 /* EncryptionResetScreenViewModelProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E8562F4D7DE073BC32902AB /* EncryptionResetScreenViewModelProtocol.swift */; };
|
||||
A0D7E5BD0298A97DCBDCE40B /* Version in Frameworks */ = {isa = PBXBuildFile; productRef = A05AF81DDD14AD58CB0E1B9B /* Version */; };
|
||||
@@ -1216,6 +1219,7 @@
|
||||
D46C33F8B61B55F0C8C2D15F /* LocationRoomTimelineItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1B2AC540DE619B36832A5DB5 /* LocationRoomTimelineItem.swift */; };
|
||||
D4CB979EB4FE26AAD9F9A72B /* UserProfileScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 604A69C081B935D6A38DE6D8 /* UserProfileScreenModels.swift */; };
|
||||
D4D7CCECC6C0AAFC42E165BB /* NotificationPermissionsScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BE9BBB18FB27F09032AD8769 /* NotificationPermissionsScreenViewModel.swift */; };
|
||||
D4F3C5656DF09037825E9965 /* ChatsSpaceFiltersScreenModels.swift in Sources */ = {isa = PBXBuildFile; fileRef = AEF2C15634499348A512A93A /* ChatsSpaceFiltersScreenModels.swift */; };
|
||||
D55AF9B5B55FEED04771A461 /* RoomFlowCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A008E57D52B07B78DFAD1BB /* RoomFlowCoordinator.swift */; };
|
||||
D5681C80D8281560AACE0035 /* Label.swift in Sources */ = {isa = PBXBuildFile; fileRef = 045253F9967A535EE5B16691 /* Label.swift */; };
|
||||
D5B1531A72387D432939D4E0 /* RoomDirectorySearchProxyProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0516C69708D5CBDE1A8E77EC /* RoomDirectorySearchProxyProtocol.swift */; };
|
||||
@@ -1304,6 +1308,7 @@
|
||||
E67418DACEDBC29E988E6ACD /* message.caf in Resources */ = {isa = PBXBuildFile; fileRef = ED482057AE39D5C6D9C5F3D8 /* message.caf */; };
|
||||
E6A2F6E4795C1054025AACFF /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = F3AB32690C6CE5C62A86D6FA /* NotificationItemProxy.swift */; };
|
||||
E6FA87F773424B27614B23E9 /* TimelineItemAccessibilityModifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1DA7E93C2E148B96EF6A8500 /* TimelineItemAccessibilityModifier.swift */; };
|
||||
E713F930C5AD0F4B11119E2B /* ChatsSpaceFiltersScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 154664B1B0D71846FBAC6E61 /* ChatsSpaceFiltersScreen.swift */; };
|
||||
E75CE800B3E64D0F7F8E228D /* TemplateScreenViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C08E9043618AE5B0BF7B07E1 /* TemplateScreenViewModelTests.swift */; };
|
||||
E77FE06B165A38BF1735509F /* SecureBackupScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF73F49E6B6683F7E2D26F0 /* SecureBackupScreenCoordinator.swift */; };
|
||||
E78D429F18071545BF661A52 /* RoomDetailsEditScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0A3E77399BD262D301451BF2 /* RoomDetailsEditScreenCoordinator.swift */; };
|
||||
@@ -1668,6 +1673,7 @@
|
||||
14517E5597594956FCE1950D /* RoomInfoProxyProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomInfoProxyProtocol.swift; sourceTree = "<group>"; };
|
||||
1454CF3AABD242F55C8A2615 /* InviteUsersScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersScreenModels.swift; sourceTree = "<group>"; };
|
||||
1511B1DCECC0DC75EB267328 /* KnockRequestsListScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockRequestsListScreen.swift; sourceTree = "<group>"; };
|
||||
154664B1B0D71846FBAC6E61 /* ChatsSpaceFiltersScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsSpaceFiltersScreen.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
@@ -2261,6 +2267,7 @@
|
||||
840182D7A61402D5947DE094 /* NotificationItemProxyMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationItemProxyMock.swift; sourceTree = "<group>"; };
|
||||
84311D707B09854D67F78BBF /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
845DDBDE5A0887E73D38B826 /* InviteUsersViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InviteUsersViewModelTests.swift; sourceTree = "<group>"; };
|
||||
847C893CF354BCBDF1351C13 /* ChatsSpaceFilterCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsSpaceFilterCell.swift; sourceTree = "<group>"; };
|
||||
848F69921527D31CAACB93AF /* SecureBackupLogoutConfirmationScreenViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupLogoutConfirmationScreenViewModelTests.swift; sourceTree = "<group>"; };
|
||||
84A00BB9CD12CF6AC98D5485 /* SecureBackupScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecureBackupScreen.swift; sourceTree = "<group>"; };
|
||||
84A87D0471D438A233C2CF4A /* RoomMemberDetailsScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMemberDetailsScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
@@ -2275,6 +2282,7 @@
|
||||
85666E40F7E817809B4FD787 /* ComposerToolbar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposerToolbar.swift; sourceTree = "<group>"; };
|
||||
8585C636A10B8141A7AE909F /* el */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = el; path = el.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
858DA81F2ACF484B7CAD6AE4 /* KnockedRoomProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KnockedRoomProxy.swift; sourceTree = "<group>"; };
|
||||
859F51637DA710BBE7B70D6D /* ChatsSpaceFiltersScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsSpaceFiltersScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
85A1941B874A3BE9CDDF43EF /* XCTestCase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XCTestCase.swift; sourceTree = "<group>"; };
|
||||
85EB16E7FE59A947CA441531 /* MediaProviderProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaProviderProtocol.swift; sourceTree = "<group>"; };
|
||||
8610C1D21565C950BCA6A454 /* AppLockSetupSettingsScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockSetupSettingsScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
@@ -2497,6 +2505,7 @@
|
||||
AE739A6836E86E3780748477 /* TimelineItemBubbleBackground.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineItemBubbleBackground.swift; sourceTree = "<group>"; };
|
||||
AEB5FF7A09B79B0C6B528F7C /* SFNumberedListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SFNumberedListView.swift; sourceTree = "<group>"; };
|
||||
AEEAFB646E583655652C3D04 /* RoomStateEventStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomStateEventStringBuilderTests.swift; sourceTree = "<group>"; };
|
||||
AEF2C15634499348A512A93A /* ChatsSpaceFiltersScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsSpaceFiltersScreenModels.swift; sourceTree = "<group>"; };
|
||||
AF25E364AE85090A70AE4644 /* AttributedStringBuilderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributedStringBuilderTests.swift; sourceTree = "<group>"; };
|
||||
AF2E6ADAE685F4109B1FE795 /* TimelineThreadSummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimelineThreadSummaryView.swift; sourceTree = "<group>"; };
|
||||
AF848B41DAF1066F3054D4A1 /* SessionVerificationScreenModels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionVerificationScreenModels.swift; sourceTree = "<group>"; };
|
||||
@@ -2792,6 +2801,7 @@
|
||||
E5FDFAA04174CC99FB66391C /* EditRoomAddressScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditRoomAddressScreenViewModel.swift; sourceTree = "<group>"; };
|
||||
E604A74AEFE0BF6151061291 /* da */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = da; path = da.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E60757AFE04391B43EA568B8 /* nl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = nl; path = nl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
E63072981793CCA84EE12798 /* ChatsSpaceFiltersScreenViewModelProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatsSpaceFiltersScreenViewModelProtocol.swift; sourceTree = "<group>"; };
|
||||
E6372DD10DED30E7AD7BCE21 /* RoomListFiltersView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomListFiltersView.swift; sourceTree = "<group>"; };
|
||||
E66763BD54A3A1D9C6E6F2F1 /* PinnedItemsIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedItemsIndicatorView.swift; sourceTree = "<group>"; };
|
||||
E6935A55AB3B0C94BC566DD6 /* EncryptionResetPasswordScreenCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncryptionResetPasswordScreenCoordinator.swift; sourceTree = "<group>"; };
|
||||
@@ -3724,6 +3734,17 @@
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
330B6E4BB92EA4AE6A95828E /* ChatsSpaceFiltersScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
AEF2C15634499348A512A93A /* ChatsSpaceFiltersScreenModels.swift */,
|
||||
859F51637DA710BBE7B70D6D /* ChatsSpaceFiltersScreenViewModel.swift */,
|
||||
E63072981793CCA84EE12798 /* ChatsSpaceFiltersScreenViewModelProtocol.swift */,
|
||||
82F77DDA38D41A185D3536F4 /* View */,
|
||||
);
|
||||
path = ChatsSpaceFiltersScreen;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
3348D14DBDB54E72FC67E2F3 /* MessageForwardingScreen */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -5137,6 +5158,15 @@
|
||||
path = Session;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
82F77DDA38D41A185D3536F4 /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
847C893CF354BCBDF1351C13 /* ChatsSpaceFilterCell.swift */,
|
||||
154664B1B0D71846FBAC6E61 /* ChatsSpaceFiltersScreen.swift */,
|
||||
);
|
||||
path = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
832EB453B3A5D04C18D86117 /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -6427,6 +6457,7 @@
|
||||
EFD4F7FCAAAB3EF45EE7A067 /* BlockedUsersScreen */,
|
||||
53FB148CD26AFB6A5B9E20B3 /* BugReportScreen */,
|
||||
1185EECDD07495D65AC84AFC /* CallScreen */,
|
||||
330B6E4BB92EA4AE6A95828E /* ChatsSpaceFiltersScreen */,
|
||||
90DC2E28718955ED87AD1456 /* CreatePollScreen */,
|
||||
821EB0D1C0019E3C7BBAEDBB /* CreateRoomScreen */,
|
||||
3E1CCC4B607946CE90B4A827 /* DeclineAndBlockScreen */,
|
||||
@@ -7934,6 +7965,11 @@
|
||||
E14E469CD97550D0FC58F3CA /* CancellableTask.swift in Sources */,
|
||||
DF8F1211F2B0B56F0FCCA5C2 /* CertificateValidatorHook.swift in Sources */,
|
||||
D885B783B95AD7832D4EF5DD /* CharacterSet.swift in Sources */,
|
||||
3E2EE5782688054B3026EF8E /* ChatsSpaceFilterCell.swift in Sources */,
|
||||
E713F930C5AD0F4B11119E2B /* ChatsSpaceFiltersScreen.swift in Sources */,
|
||||
D4F3C5656DF09037825E9965 /* ChatsSpaceFiltersScreenModels.swift in Sources */,
|
||||
8A2B2836B1B0CCA3CC140560 /* ChatsSpaceFiltersScreenViewModel.swift in Sources */,
|
||||
A076E0A9338FD2D950C3C4A1 /* ChatsSpaceFiltersScreenViewModelProtocol.swift in Sources */,
|
||||
572474C7CA4B03FF0B5DF548 /* ChatsTabFlowCoordinator.swift in Sources */,
|
||||
BC5F94B10B40ABEC6046B473 /* ChatsTabFlowCoordinatorStateMachine.swift in Sources */,
|
||||
A52090A4FE0DB826578DFC03 /* Client.swift in Sources */,
|
||||
|
||||
@@ -541,10 +541,14 @@
|
||||
"screen_create_room_public_option_short_description" = "Anyone can join.";
|
||||
"screen_create_room_room_access_section_knocking_option_description" = "Anyone can ask to join but an administrator or a moderator must accept the request.";
|
||||
"screen_create_room_room_access_section_knocking_option_title" = "Allow ask to join";
|
||||
"screen_create_room_room_access_section_knocking_restricted_option_description" = "Anyone in %1$@ can join but everyone else must request access.";
|
||||
"screen_create_room_room_access_section_knocking_restricted_option_title" = "Ask to join";
|
||||
"screen_create_room_room_access_section_private_option_description" = "Only people invited can join.";
|
||||
"screen_create_room_room_access_section_private_option_title" = "Private";
|
||||
"screen_create_room_room_access_section_public_option_description" = "Anyone can join.";
|
||||
"screen_create_room_room_access_section_public_option_title" = "Public";
|
||||
"screen_create_room_room_access_section_restricted_option_description" = "Anyone in %1$@ can join.";
|
||||
"screen_create_room_room_access_section_restricted_option_title" = "Standard";
|
||||
"screen_create_room_room_access_section_title" = "Who has access";
|
||||
"screen_create_room_room_address_section_footer" = "You’ll need an address in order to make it visible in the public directory.";
|
||||
"screen_create_room_room_address_section_title" = "Address";
|
||||
|
||||
@@ -541,10 +541,14 @@
|
||||
"screen_create_room_public_option_short_description" = "Anyone can join.";
|
||||
"screen_create_room_room_access_section_knocking_option_description" = "Anyone can ask to join but an administrator or a moderator must accept the request.";
|
||||
"screen_create_room_room_access_section_knocking_option_title" = "Allow ask to join";
|
||||
"screen_create_room_room_access_section_knocking_restricted_option_description" = "Anyone in %1$@ can join but everyone else must request access.";
|
||||
"screen_create_room_room_access_section_knocking_restricted_option_title" = "Ask to join";
|
||||
"screen_create_room_room_access_section_private_option_description" = "Only people invited can join.";
|
||||
"screen_create_room_room_access_section_private_option_title" = "Private";
|
||||
"screen_create_room_room_access_section_public_option_description" = "Anyone can join.";
|
||||
"screen_create_room_room_access_section_public_option_title" = "Public";
|
||||
"screen_create_room_room_access_section_restricted_option_description" = "Anyone in %1$@ can join.";
|
||||
"screen_create_room_room_access_section_restricted_option_title" = "Standard";
|
||||
"screen_create_room_room_access_section_title" = "Who has access";
|
||||
"screen_create_room_room_address_section_footer" = "You’ll need an address in order to make it visible in the public directory.";
|
||||
"screen_create_room_room_address_section_title" = "Address";
|
||||
|
||||
@@ -67,6 +67,7 @@ final class AppSettings {
|
||||
case linkPreviewsEnabled
|
||||
case focusEventOnNotificationTap
|
||||
case linkNewDeviceEnabled
|
||||
case spaceFiltersEnabled
|
||||
|
||||
// Spaces
|
||||
case spaceSettingsEnabled
|
||||
@@ -420,6 +421,9 @@ final class AppSettings {
|
||||
@UserPreference(key: UserDefaultsKeys.linkNewDeviceEnabled, defaultValue: false, storageType: .userDefaults(store))
|
||||
var linkNewDeviceEnabled
|
||||
|
||||
@UserPreference(key: UserDefaultsKeys.spaceFiltersEnabled, defaultValue: false, storageType: .userDefaults(store))
|
||||
var spaceFiltersEnabled
|
||||
|
||||
@UserPreference(key: UserDefaultsKeys.developerOptionsEnabled, defaultValue: isDevelopmentBuild, storageType: .userDefaults(store))
|
||||
var developerOptionsEnabled
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ class ChatsTabFlowCoordinatorStateMachine {
|
||||
|
||||
case declineAndBlockUserScreen(detailState: DetailState?)
|
||||
|
||||
/// The selected room ID from the state if available.
|
||||
/// The state of the currently selected room
|
||||
var detailState: DetailState? {
|
||||
switch self {
|
||||
case .initial, .userProfileScreen, .shareExtensionRoomList:
|
||||
|
||||
@@ -1606,6 +1606,12 @@ internal enum L10n {
|
||||
internal static var screenCreateRoomRoomAccessSectionKnockingOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_knocking_option_description") }
|
||||
/// Allow ask to join
|
||||
internal static var screenCreateRoomRoomAccessSectionKnockingOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_knocking_option_title") }
|
||||
/// Anyone in %1$@ can join but everyone else must request access.
|
||||
internal static func screenCreateRoomRoomAccessSectionKnockingRestrictedOptionDescription(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "screen_create_room_room_access_section_knocking_restricted_option_description", String(describing: p1))
|
||||
}
|
||||
/// Ask to join
|
||||
internal static var screenCreateRoomRoomAccessSectionKnockingRestrictedOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_knocking_restricted_option_title") }
|
||||
/// Only people invited can join.
|
||||
internal static var screenCreateRoomRoomAccessSectionPrivateOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_private_option_description") }
|
||||
/// Private
|
||||
@@ -1614,6 +1620,12 @@ internal enum L10n {
|
||||
internal static var screenCreateRoomRoomAccessSectionPublicOptionDescription: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_public_option_description") }
|
||||
/// Public
|
||||
internal static var screenCreateRoomRoomAccessSectionPublicOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_public_option_title") }
|
||||
/// Anyone in %1$@ can join.
|
||||
internal static func screenCreateRoomRoomAccessSectionRestrictedOptionDescription(_ p1: Any) -> String {
|
||||
return L10n.tr("Localizable", "screen_create_room_room_access_section_restricted_option_description", String(describing: p1))
|
||||
}
|
||||
/// Standard
|
||||
internal static var screenCreateRoomRoomAccessSectionRestrictedOptionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_restricted_option_title") }
|
||||
/// Who has access
|
||||
internal static var screenCreateRoomRoomAccessSectionTitle: String { return L10n.tr("Localizable", "screen_create_room_room_access_section_title") }
|
||||
/// You’ll need an address in order to make it visible in the public directory.
|
||||
|
||||
@@ -16814,6 +16814,11 @@ class SpaceServiceProxyMock: SpaceServiceProxyProtocol, @unchecked Sendable {
|
||||
set(value) { underlyingTopLevelSpacesPublisher = value }
|
||||
}
|
||||
var underlyingTopLevelSpacesPublisher: CurrentValuePublisher<[SpaceServiceRoomProtocol], Never>!
|
||||
var spaceFilterPublisher: CurrentValuePublisher<[SpaceServiceFilter], Never> {
|
||||
get { return underlyingSpaceFilterPublisher }
|
||||
set(value) { underlyingSpaceFilterPublisher = value }
|
||||
}
|
||||
var underlyingSpaceFilterPublisher: CurrentValuePublisher<[SpaceServiceFilter], Never>!
|
||||
|
||||
//MARK: - spaceRoomList
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ extension RoomSummaryProviderMock {
|
||||
}
|
||||
|
||||
roomListSubject.send(rooms)
|
||||
case let .all(filters):
|
||||
case let .rooms(_, filters), let .all(filters):
|
||||
var rooms = initialRooms
|
||||
|
||||
if filters.count > 1 {
|
||||
|
||||
@@ -60,6 +60,7 @@ extension [SpaceServiceRoomProtocol] {
|
||||
isSpace: true,
|
||||
childrenCount: 1,
|
||||
joinedMembersCount: 500,
|
||||
canonicalAlias: "#the-foundation:matrix.org",
|
||||
state: .joined)),
|
||||
SpaceServiceRoomMock(.init(id: "space2",
|
||||
name: "The Second Foundation",
|
||||
@@ -72,6 +73,7 @@ extension [SpaceServiceRoomProtocol] {
|
||||
isSpace: true,
|
||||
childrenCount: 25000,
|
||||
joinedMembersCount: 1_000_000_000,
|
||||
canonicalAlias: "#the-galactic-empire:matrix.org",
|
||||
state: .joined)),
|
||||
SpaceServiceRoomMock(.init(id: "space4",
|
||||
name: "The Korellians",
|
||||
|
||||
@@ -14,6 +14,7 @@ import MatrixRustSDKMocks
|
||||
extension SpaceServiceProxyMock {
|
||||
struct Configuration {
|
||||
var topLevelSpaces: [SpaceServiceRoomProtocol] = []
|
||||
var spaceFilters: [SpaceServiceFilter] = []
|
||||
var joinedParentSpaces: [SpaceServiceRoomProtocol] = []
|
||||
var spaceRoomLists: [String: SpaceRoomListProxyMock] = [:]
|
||||
var leaveSpaceRooms: [LeaveSpaceRoom] = []
|
||||
@@ -23,6 +24,8 @@ extension SpaceServiceProxyMock {
|
||||
self.init()
|
||||
|
||||
topLevelSpacesPublisher = .init(configuration.topLevelSpaces)
|
||||
spaceFilterPublisher = .init(configuration.spaceFilters)
|
||||
|
||||
joinedParentsChildIDReturnValue = .success(configuration.joinedParentSpaces)
|
||||
spaceRoomListSpaceIDClosure = { spaceID in
|
||||
if let spaceRoomList = configuration.spaceRoomLists[spaceID] {
|
||||
@@ -31,6 +34,7 @@ extension SpaceServiceProxyMock {
|
||||
.failure(.sdkError(ClientProxyMockError.generic))
|
||||
}
|
||||
}
|
||||
|
||||
leaveSpaceSpaceIDClosure = { spaceID in
|
||||
.success(LeaveSpaceHandleProxy(spaceID: spaceID,
|
||||
leaveHandle: LeaveSpaceHandleSDKMock(.init(rooms: configuration.leaveSpaceRooms))))
|
||||
@@ -45,13 +49,21 @@ extension SpaceServiceProxyMock {
|
||||
|
||||
extension SpaceServiceProxyMock.Configuration {
|
||||
static var populated: SpaceServiceProxyMock.Configuration {
|
||||
let spaceFilters = [SpaceServiceRoomProtocol].mockJoinedSpaces.reduce(into: [SpaceServiceFilter]()) { partialResult, spaceRoom in
|
||||
partialResult.append(SpaceServiceFilter(room: spaceRoom, level: 0, descendants: .init()))
|
||||
partialResult.append(SpaceServiceFilter(room: spaceRoom, level: 1, descendants: .init()))
|
||||
}
|
||||
|
||||
let spaceRoomLists = [SpaceServiceRoomProtocol].mockJoinedSpaces.map {
|
||||
($0.id, SpaceRoomListProxyMock(.init(spaceServiceRoom: $0, initialSpaceRooms: .mockSpaceList)))
|
||||
}
|
||||
|
||||
let subSpaceRoomLists = [SpaceServiceRoomProtocol].mockSpaceList.map {
|
||||
($0.id, SpaceRoomListProxyMock(.init(spaceServiceRoom: $0, initialSpaceRooms: .mockSingleRoom)))
|
||||
}
|
||||
|
||||
return .init(topLevelSpaces: .mockJoinedSpaces, spaceRoomLists: .init(uniqueKeysWithValues: spaceRoomLists + subSpaceRoomLists))
|
||||
return .init(topLevelSpaces: .mockJoinedSpaces,
|
||||
spaceFilters: spaceFilters,
|
||||
spaceRoomLists: .init(uniqueKeysWithValues: spaceRoomLists + subSpaceRoomLists))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ enum A11yIdentifiers {
|
||||
let userAvatar = "home_screen-user_avatar"
|
||||
let recoveryKeyConfirmationBannerContinue = "home_screen-recovery_key_confirmation_continue"
|
||||
let startChat = "home_screen-start_chat"
|
||||
let spaceFilters = "home_screen-space_filters"
|
||||
|
||||
let roomNamePrefix = "home_screen-room_name"
|
||||
func roomName(_ name: String) -> String {
|
||||
|
||||
@@ -141,6 +141,7 @@ enum RoomAvatarSizeOnScreen {
|
||||
case chats
|
||||
case spaces
|
||||
case spaceSettings
|
||||
case spaceFilters
|
||||
case authorizedSpaces
|
||||
case timeline
|
||||
case leaveSpace
|
||||
@@ -161,7 +162,7 @@ enum RoomAvatarSizeOnScreen {
|
||||
case .chats, .spaces, .spaceSettings:
|
||||
return 52
|
||||
case .timeline, .leaveSpace, .roomDirectorySearch,
|
||||
.completionSuggestions, .authorizedSpaces:
|
||||
.completionSuggestions, .authorizedSpaces, .spaceFilters:
|
||||
return 32
|
||||
case .notificationSettings:
|
||||
return 30
|
||||
|
||||
@@ -113,6 +113,10 @@ extension SDKListener: SpaceRoomListSpaceListener where T == SpaceRoom? {
|
||||
func onUpdate(space: SpaceRoom?) { onUpdateClosure(space) }
|
||||
}
|
||||
|
||||
extension SDKListener: SpaceServiceSpaceFiltersListener where T == [SpaceFilterUpdate] {
|
||||
func onUpdate(filterUpdates: [SpaceFilterUpdate]) { onUpdateClosure(filterUpdates) }
|
||||
}
|
||||
|
||||
// MARK: Room
|
||||
|
||||
extension SDKListener: RoomInfoListener where T == RoomInfo {
|
||||
|
||||
@@ -26,6 +26,8 @@ enum TestablePreviewsDictionary {
|
||||
"BugReportScreen_Previews" : BugReportScreen_Previews.self,
|
||||
"CallInviteRoomTimelineView_Previews" : CallInviteRoomTimelineView_Previews.self,
|
||||
"CallNotificationRoomTimelineView_Previews" : CallNotificationRoomTimelineView_Previews.self,
|
||||
"ChatsSpaceFilterCell_Previews" : ChatsSpaceFilterCell_Previews.self,
|
||||
"ChatsSpaceFiltersScreen_Previews" : ChatsSpaceFiltersScreen_Previews.self,
|
||||
"CollapsibleRoomTimelineView_Previews" : CollapsibleRoomTimelineView_Previews.self,
|
||||
"CompletionSuggestion_Previews" : CompletionSuggestion_Previews.self,
|
||||
"ComposerToolbar_Previews" : ComposerToolbar_Previews.self,
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations 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 ChatsSpaceFiltersScreenViewModelAction {
|
||||
case confirm(SpaceServiceFilter)
|
||||
case cancel
|
||||
}
|
||||
|
||||
struct ChatsSpaceFiltersScreenViewState: BindableState {
|
||||
var filters = [SpaceServiceFilter]()
|
||||
var bindings: ChatsSpaceFiltersScreenViewStateBindings
|
||||
}
|
||||
|
||||
struct ChatsSpaceFiltersScreenViewStateBindings { }
|
||||
|
||||
enum ChatsSpaceFiltersScreenViewAction: CustomStringConvertible {
|
||||
case confirm(SpaceServiceFilter)
|
||||
case cancel
|
||||
|
||||
var description: String {
|
||||
switch self {
|
||||
case .confirm: "Confirm"
|
||||
case .cancel: "Cancel"
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations 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 ChatsSpaceFiltersScreenViewModelType = StateStoreViewModelV2<ChatsSpaceFiltersScreenViewState, ChatsSpaceFiltersScreenViewAction>
|
||||
|
||||
class ChatsSpaceFiltersScreenViewModel: ChatsSpaceFiltersScreenViewModelType, ChatsSpaceFiltersScreenViewModelProtocol, Identifiable {
|
||||
private let spaceService: SpaceServiceProxyProtocol
|
||||
|
||||
private let actionsSubject: PassthroughSubject<ChatsSpaceFiltersScreenViewModelAction, Never> = .init()
|
||||
var actionsPublisher: AnyPublisher<ChatsSpaceFiltersScreenViewModelAction, Never> {
|
||||
actionsSubject.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
let id = UUID()
|
||||
|
||||
init(spaceService: SpaceServiceProxyProtocol,
|
||||
mediaProvider: MediaProviderProtocol) {
|
||||
self.spaceService = spaceService
|
||||
|
||||
super.init(initialViewState: ChatsSpaceFiltersScreenViewState(bindings: .init()),
|
||||
mediaProvider: mediaProvider)
|
||||
|
||||
state.filters = spaceService.spaceFilterPublisher.value
|
||||
|
||||
spaceService.spaceFilterPublisher.sink { [weak self] filters in
|
||||
self?.state.filters = filters
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
|
||||
// MARK: - Public
|
||||
|
||||
override func process(viewAction: ChatsSpaceFiltersScreenViewAction) {
|
||||
MXLog.info("View model: received view action: \(viewAction)")
|
||||
|
||||
switch viewAction {
|
||||
case .confirm(let filter):
|
||||
actionsSubject.send(.confirm(filter))
|
||||
case .cancel:
|
||||
actionsSubject.send(.cancel)
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations 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 ChatsSpaceFiltersScreenViewModelProtocol {
|
||||
var actionsPublisher: AnyPublisher<ChatsSpaceFiltersScreenViewModelAction, Never> { get }
|
||||
var context: ChatsSpaceFiltersScreenViewModelType.Context { get }
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
import Combine
|
||||
import Compound
|
||||
import SwiftUI
|
||||
|
||||
struct ChatsSpaceFilterCell: View {
|
||||
@Environment(\.dynamicTypeSize) var dynamicTypeSize
|
||||
|
||||
let filter: SpaceServiceFilter
|
||||
let mediaProvider: MediaProviderProtocol!
|
||||
|
||||
private let verticalInsets = 12.0
|
||||
private let horizontalInsets = 16.0
|
||||
|
||||
let action: (SpaceServiceFilter) -> Void
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
action(filter)
|
||||
} label: {
|
||||
HStack(spacing: 12.0) {
|
||||
HStack(spacing: 8.0) {
|
||||
if filter.level > 0 {
|
||||
Spacer(minLength: 16 * CGFloat(filter.level))
|
||||
}
|
||||
|
||||
HStack(spacing: 12.0) {
|
||||
avatar
|
||||
|
||||
content
|
||||
.padding(.vertical, verticalInsets)
|
||||
.overlay(alignment: .bottom) {
|
||||
Rectangle()
|
||||
.fill(Color.compound.borderDisabled)
|
||||
.frame(height: 1 / UIScreen.main.scale)
|
||||
.padding(.trailing, -horizontalInsets)
|
||||
}
|
||||
}
|
||||
.accessibilityElement(children: .combine)
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, horizontalInsets)
|
||||
}
|
||||
|
||||
@ViewBuilder @MainActor
|
||||
private var avatar: some View {
|
||||
if dynamicTypeSize < .accessibility3 {
|
||||
RoomAvatarImage(avatar: filter.room.avatar,
|
||||
avatarSize: .room(on: .spaceFilters),
|
||||
mediaProvider: mediaProvider)
|
||||
.dynamicTypeSize(dynamicTypeSize < .accessibility1 ? dynamicTypeSize : .accessibility1)
|
||||
.accessibilityHidden(true)
|
||||
}
|
||||
}
|
||||
|
||||
private var content: some View {
|
||||
HStack(spacing: 16) {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(filter.room.name)
|
||||
.font(.compound.bodyLG)
|
||||
.foregroundColor(.compound.textPrimary)
|
||||
.lineLimit(1)
|
||||
|
||||
ZStack {
|
||||
// Hidden text to maintain consistent height.
|
||||
Text("")
|
||||
.hidden()
|
||||
|
||||
if let alias = filter.room.canonicalAlias {
|
||||
Text(alias)
|
||||
.font(.compound.bodyMD)
|
||||
.foregroundColor(.compound.textSecondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatsSpaceFilterCell_Previews: PreviewProvider, TestablePreview {
|
||||
static let mediaProvider = MediaProviderMock(configuration: .init())
|
||||
|
||||
static let spaces = [SpaceServiceRoomProtocol].mockJoinedSpaces2
|
||||
|
||||
static var previews: some View {
|
||||
VStack(spacing: 0) {
|
||||
ForEach(spaces, id: \.id) { space in
|
||||
ChatsSpaceFilterCell(filter: .init(room: space, level: 0, descendants: .init()),
|
||||
mediaProvider: mediaProvider) { _ in }
|
||||
ChatsSpaceFilterCell(filter: .init(room: space, level: 1, descendants: .init()),
|
||||
mediaProvider: mediaProvider) { _ in }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
//
|
||||
// Copyright 2025 Element Creations 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 ChatsSpaceFiltersScreen: View {
|
||||
@Bindable var context: ChatsSpaceFiltersScreenViewModel.Context
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
ScrollView {
|
||||
LazyVStack(spacing: 0) {
|
||||
ForEach(context.viewState.filters) { filter in
|
||||
ChatsSpaceFilterCell(filter: filter,
|
||||
mediaProvider: context.mediaProvider) { filter in
|
||||
context.send(viewAction: .confirm(filter))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.toolbar { toolbar }
|
||||
.navigationTitle(L10n.screenRoomlistYourSpaces)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
.presentationDragIndicator(.visible)
|
||||
}
|
||||
|
||||
@ToolbarContentBuilder
|
||||
private var toolbar: some ToolbarContent {
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
ToolbarButton(role: .cancel) {
|
||||
context.send(viewAction: .cancel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct ChatsSpaceFiltersScreen_Previews: PreviewProvider, TestablePreview {
|
||||
static let viewModel = makeViewModel()
|
||||
|
||||
static var previews: some View {
|
||||
ChatsSpaceFiltersScreen(context: viewModel.context)
|
||||
}
|
||||
|
||||
static func makeViewModel() -> ChatsSpaceFiltersScreenViewModel {
|
||||
ChatsSpaceFiltersScreenViewModel(spaceService: SpaceServiceProxyMock(.populated),
|
||||
mediaProvider: MediaProviderMock(configuration: .init()))
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,7 @@ enum HomeScreenViewAction {
|
||||
case dismissNewSoundBanner
|
||||
case updateVisibleItemRange(Range<Int>)
|
||||
case globalSearch
|
||||
case spaceFilters
|
||||
case markRoomAsUnread(roomIdentifier: String)
|
||||
case markRoomAsRead(roomIdentifier: String)
|
||||
case markRoomAsFavourite(roomIdentifier: String, isFavourite: Bool)
|
||||
@@ -109,6 +110,10 @@ struct HomeScreenViewState: BindableState {
|
||||
|
||||
var reportRoomEnabled = false
|
||||
|
||||
var spaceFiltersEnabled = false
|
||||
|
||||
var selectedSpaceFilter: SpaceServiceFilter?
|
||||
|
||||
var visibleRooms: [HomeScreenRoom] {
|
||||
if roomListMode == .skeletons {
|
||||
return placeholderRooms
|
||||
@@ -150,6 +155,8 @@ struct HomeScreenViewStateBindings {
|
||||
|
||||
var alertInfo: AlertInfo<UUID>?
|
||||
var leaveRoomAlertItem: LeaveRoomAlertItem?
|
||||
|
||||
var spaceFiltersViewModel: ChatsSpaceFiltersScreenViewModel?
|
||||
}
|
||||
|
||||
struct HomeScreenRoom: Identifiable, Equatable {
|
||||
|
||||
@@ -15,6 +15,7 @@ typealias HomeScreenViewModelType = StateStoreViewModel<HomeScreenViewState, Hom
|
||||
|
||||
class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol {
|
||||
private let userSession: UserSessionProtocol
|
||||
private let spaceFilterSubject: CurrentValueSubject<SpaceServiceFilter?, Never>
|
||||
private let analyticsService: AnalyticsService
|
||||
private let appSettings: AppSettings
|
||||
private let notificationManager: NotificationManagerProtocol
|
||||
@@ -39,9 +40,12 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
self.notificationManager = notificationManager
|
||||
self.userIndicatorController = userIndicatorController
|
||||
|
||||
spaceFilterSubject = CurrentValueSubject<SpaceServiceFilter?, Never>(nil)
|
||||
|
||||
roomSummaryProvider = userSession.clientProxy.roomSummaryProvider
|
||||
|
||||
super.init(initialViewState: .init(userID: userSession.clientProxy.userID,
|
||||
spaceFiltersEnabled: appSettings.spaceFiltersEnabled,
|
||||
bindings: .init(filtersState: .init(appSettings: appSettings))),
|
||||
mediaProvider: userSession.mediaProvider)
|
||||
|
||||
@@ -112,12 +116,22 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
appSettings.$spaceFiltersEnabled
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.spaceFiltersEnabled, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
userSession.clientProxy.hideInviteAvatarsPublisher
|
||||
.removeDuplicates()
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.hideInviteAvatars, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
spaceFilterSubject
|
||||
.receive(on: DispatchQueue.main)
|
||||
.weakAssign(to: \.state.selectedSpaceFilter, on: self)
|
||||
.store(in: &cancellables)
|
||||
|
||||
Task {
|
||||
state.reportRoomEnabled = await userSession.clientProxy.isReportRoomSupported
|
||||
}
|
||||
@@ -126,9 +140,9 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
let searchQuery = context.$viewState.map(\.bindings.searchQuery)
|
||||
let activeFilters = context.$viewState.map(\.bindings.filtersState.activeFilters)
|
||||
isSearchFieldFocused
|
||||
.combineLatest(searchQuery, activeFilters)
|
||||
.combineLatest(searchQuery, activeFilters, spaceFilterSubject)
|
||||
.removeDuplicates { $0 == $1 }
|
||||
.sink { [weak self] isSearchFieldFocused, _, _ in
|
||||
.sink { [weak self] isSearchFieldFocused, _, _, _ in
|
||||
guard let self else { return }
|
||||
// isSearchFieldFocused` is sometimes turning to true after cancelling the search. So to be extra sure we are updating the values correctly we read them directly in the next run loop, and we add a small delay if the value has changed
|
||||
let delay = isSearchFieldFocused == self.context.viewState.bindings.isSearchFieldFocused ? 0.0 : 0.05
|
||||
@@ -175,6 +189,26 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
actionsSubject.send(.presentStartChatScreen)
|
||||
case .globalSearch:
|
||||
actionsSubject.send(.presentGlobalSearch)
|
||||
case .spaceFilters:
|
||||
if spaceFilterSubject.value != nil {
|
||||
spaceFilterSubject.send(nil)
|
||||
} else {
|
||||
state.bindings.spaceFiltersViewModel = ChatsSpaceFiltersScreenViewModel(spaceService: userSession.clientProxy.spaceService,
|
||||
mediaProvider: userSession.mediaProvider)
|
||||
|
||||
state.bindings.spaceFiltersViewModel?.actionsPublisher.sink { [weak self] action in
|
||||
guard let self else { return }
|
||||
|
||||
switch action {
|
||||
case .confirm(let spaceServiceFilter):
|
||||
spaceFilterSubject.send(spaceServiceFilter)
|
||||
state.bindings.spaceFiltersViewModel = nil
|
||||
case .cancel:
|
||||
state.bindings.spaceFiltersViewModel = nil
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
case .markRoomAsUnread(let roomIdentifier):
|
||||
Task {
|
||||
guard case let .joined(roomProxy) = await userSession.clientProxy.roomForIdentifier(roomIdentifier) else {
|
||||
@@ -242,7 +276,12 @@ class HomeScreenViewModel: HomeScreenViewModelType, HomeScreenViewModelProtocol
|
||||
if state.bindings.isSearchFieldFocused {
|
||||
roomSummaryProvider?.setFilter(.search(query: state.bindings.searchQuery))
|
||||
} else {
|
||||
roomSummaryProvider?.setFilter(.all(filters: state.bindings.filtersState.activeFilters.set))
|
||||
if let spaceFilter = spaceFilterSubject.value {
|
||||
roomSummaryProvider?.setFilter(.rooms(roomsIDs: spaceFilter.descendants,
|
||||
filters: state.bindings.filtersState.activeFilters.set))
|
||||
} else {
|
||||
roomSummaryProvider?.setFilter(.all(filters: state.bindings.filtersState.activeFilters.set))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,22 +16,40 @@ struct HomeScreen: View {
|
||||
|
||||
@State private var scrollViewAdapter = ScrollViewAdapter()
|
||||
|
||||
@Namespace private var navigationTransitionNamespace
|
||||
private enum NavigationTransitionSourceID {
|
||||
case spaceFilters
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
HomeScreenContent(context: context, scrollViewAdapter: scrollViewAdapter)
|
||||
.alert(item: $context.alertInfo)
|
||||
.alert(item: $context.leaveRoomAlertItem,
|
||||
actions: leaveRoomAlertActions,
|
||||
message: leaveRoomAlertMessage)
|
||||
.navigationTitle(L10n.screenRoomlistMainSpaceTitle)
|
||||
.navigationTitle(title)
|
||||
.toolbar { toolbar }
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.track(screen: .Home)
|
||||
.toolbarBloom(hasSearchBar: true)
|
||||
.sentryTrace("\(Self.self)")
|
||||
.sheet(item: $context.spaceFiltersViewModel) { vm in
|
||||
ChatsSpaceFiltersScreen(context: vm.context)
|
||||
.navigationTransition(.zoom(sourceID: NavigationTransitionSourceID.spaceFilters,
|
||||
in: navigationTransitionNamespace))
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private var title: String {
|
||||
if let selectedSpace = context.viewState.selectedSpaceFilter {
|
||||
selectedSpace.room.name
|
||||
} else {
|
||||
L10n.screenRoomlistMainSpaceTitle
|
||||
}
|
||||
}
|
||||
|
||||
@ToolbarContentBuilder
|
||||
private var toolbar: some ToolbarContent {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
@@ -47,6 +65,20 @@ struct HomeScreen: View {
|
||||
.buttonStyle(.compound(.super, size: .toolbarIcon))
|
||||
}
|
||||
}
|
||||
|
||||
if context.viewState.spaceFiltersEnabled {
|
||||
if #available(iOS 26, *) {
|
||||
ToolbarSpacer(.fixed, placement: .primaryAction)
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
SpaceFiltersButton(selected: context.viewState.selectedSpaceFilter != nil) {
|
||||
context.send(viewAction: .spaceFilters)
|
||||
}
|
||||
.matchedTransitionSource(id: NavigationTransitionSourceID.spaceFilters,
|
||||
in: navigationTransitionNamespace)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var settingsButton: some View {
|
||||
@@ -93,6 +125,42 @@ struct HomeScreen: View {
|
||||
private func leaveRoomAlertMessage(_ item: LeaveRoomAlertItem) -> some View {
|
||||
Text(item.subtitle)
|
||||
}
|
||||
|
||||
private struct SpaceFiltersButton: View {
|
||||
var selected = false
|
||||
var action: () -> Void
|
||||
|
||||
var body: some View {
|
||||
if #available(iOS 26, *) {
|
||||
if selected {
|
||||
content
|
||||
.backportButtonStyleGlassProminent()
|
||||
.tint(.compound.bgAccentRest)
|
||||
} else {
|
||||
content
|
||||
}
|
||||
} else {
|
||||
if selected {
|
||||
content
|
||||
.buttonStyle(.compound(.primary, size: .toolbarIcon))
|
||||
} else {
|
||||
content
|
||||
.buttonStyle(.compound(.tertiary, size: .toolbarIcon))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var content: some View {
|
||||
Button {
|
||||
action()
|
||||
} label: {
|
||||
CompoundIcon(\.filter)
|
||||
}
|
||||
.accessibilityLabel(L10n.screenRoomlistYourSpaces)
|
||||
.accessibilityAddTraits(selected ? .isSelected : [])
|
||||
.accessibilityIdentifier(A11yIdentifiers.homeScreen.spaceFilters)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
+1
@@ -70,6 +70,7 @@ protocol DeveloperOptionsProtocol: AnyObject {
|
||||
|
||||
var spaceSettingsEnabled: Bool { get set }
|
||||
var createSpaceEnabled: Bool { get set }
|
||||
var spaceFiltersEnabled: Bool { get set }
|
||||
}
|
||||
|
||||
extension AppSettings: DeveloperOptionsProtocol { }
|
||||
|
||||
+4
@@ -52,6 +52,10 @@ struct DeveloperOptionsScreen: View {
|
||||
}
|
||||
|
||||
Section("Spaces") {
|
||||
Toggle(isOn: $context.spaceFiltersEnabled) {
|
||||
Text("Space filters")
|
||||
}
|
||||
|
||||
Toggle(isOn: $context.spaceSettingsEnabled) {
|
||||
Text("Space settings")
|
||||
}
|
||||
|
||||
@@ -133,6 +133,18 @@ class RoomSummaryProvider: RoomSummaryProviderProtocol {
|
||||
[.normalizedMatchRoomName(pattern: query)] + baseFilter
|
||||
}
|
||||
_ = listUpdatesSubscriptionResult?.controller().setFilter(kind: .all(filters: filters))
|
||||
case .rooms(let roomIDs, let filters):
|
||||
var rustFilters = filters.map(\.rustFilter) + baseFilter
|
||||
|
||||
if !roomIDs.isEmpty {
|
||||
rustFilters.append(.identifiers(identifiers: Array(roomIDs)))
|
||||
}
|
||||
|
||||
if !filters.contains(.lowPriority), appSettings.lowPriorityFilterEnabled {
|
||||
rustFilters.append(.nonLowPriority)
|
||||
}
|
||||
|
||||
_ = listUpdatesSubscriptionResult?.controller().setFilter(kind: .all(filters: rustFilters))
|
||||
case let .all(filters):
|
||||
var rustFilters = filters.map(\.rustFilter) + baseFilter
|
||||
|
||||
|
||||
@@ -40,6 +40,8 @@ enum RoomSummaryProviderFilter: Equatable {
|
||||
case search(query: String)
|
||||
/// Includes only what satisfies the filters used
|
||||
case all(filters: Set<RoomListFilter>)
|
||||
/// Include only rooms from the given that satisfy the given filters
|
||||
case rooms(roomsIDs: Set<String>, filters: Set<RoomListFilter>)
|
||||
}
|
||||
|
||||
// sourcery: AutoMockable
|
||||
|
||||
@@ -19,6 +19,12 @@ class SpaceServiceProxy: SpaceServiceProxyProtocol {
|
||||
spacesSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
private var spaceFilterHandle: TaskHandle?
|
||||
private let spaceFilterSubject = CurrentValueSubject<[SpaceServiceFilter], Never>([])
|
||||
var spaceFilterPublisher: CurrentValuePublisher<[SpaceServiceFilter], Never> {
|
||||
spaceFilterSubject.asCurrentValuePublisher()
|
||||
}
|
||||
|
||||
init(spaceService: SpaceServiceProtocol) {
|
||||
self.spaceService = spaceService
|
||||
|
||||
@@ -27,7 +33,11 @@ class SpaceServiceProxy: SpaceServiceProxyProtocol {
|
||||
|
||||
private func setupSubscriptions() async {
|
||||
topLevelSpacesHandle = await spaceService.subscribeToTopLevelJoinedSpaces(listener: SDKListener { [weak self] updates in
|
||||
self?.handleUpdates(updates)
|
||||
self?.handleSpaceListUpdates(updates)
|
||||
})
|
||||
|
||||
spaceFilterHandle = await spaceService.subscribeToSpaceFilters(listener: SDKListener { [weak self] updates in
|
||||
self?.handleSpaceFilterUpdates(updates)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -87,7 +97,7 @@ class SpaceServiceProxy: SpaceServiceProxyProtocol {
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func handleUpdates(_ updates: [SpaceListUpdate]) {
|
||||
private func handleSpaceListUpdates(_ updates: [SpaceListUpdate]) {
|
||||
var spaces = spacesSubject.value
|
||||
|
||||
for update in updates {
|
||||
@@ -119,4 +129,37 @@ class SpaceServiceProxy: SpaceServiceProxyProtocol {
|
||||
|
||||
spacesSubject.send(spaces)
|
||||
}
|
||||
|
||||
private func handleSpaceFilterUpdates(_ updates: [SpaceFilterUpdate]) {
|
||||
var filters = spaceFilterSubject.value
|
||||
|
||||
for update in updates {
|
||||
switch update {
|
||||
case .append(let spaceFilters):
|
||||
filters.append(contentsOf: spaceFilters.map(SpaceServiceFilter.init))
|
||||
case .clear:
|
||||
filters.removeAll()
|
||||
case .pushFront(let filter):
|
||||
filters.insert(SpaceServiceFilter(filter: filter), at: 0)
|
||||
case .pushBack(let filter):
|
||||
filters.append(SpaceServiceFilter(filter: filter))
|
||||
case .popFront:
|
||||
filters.removeFirst()
|
||||
case .popBack:
|
||||
filters.removeLast()
|
||||
case .insert(let index, let filter):
|
||||
filters.insert(SpaceServiceFilter(filter: filter), at: Int(index))
|
||||
case .set(let index, let filter):
|
||||
filters[Int(index)] = SpaceServiceFilter(filter: filter)
|
||||
case .remove(let index):
|
||||
filters.remove(at: Int(index))
|
||||
case .truncate(let length):
|
||||
filters.removeSubrange(Int(length)..<filters.count)
|
||||
case .reset(let spaceFilters):
|
||||
filters = spaceFilters.map(SpaceServiceFilter.init)
|
||||
}
|
||||
}
|
||||
|
||||
spaceFilterSubject.send(filters)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,15 +7,44 @@
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import MatrixRustSDK
|
||||
|
||||
enum SpaceServiceProxyError: Error {
|
||||
case sdkError(Error)
|
||||
case missingSpace
|
||||
}
|
||||
|
||||
struct SpaceServiceFilter: Identifiable, Equatable {
|
||||
let room: SpaceServiceRoomProtocol
|
||||
let level: UInt
|
||||
let descendants: Set<String>
|
||||
|
||||
init(room: SpaceServiceRoomProtocol, level: UInt, descendants: Set<String>) {
|
||||
self.room = room
|
||||
self.level = level
|
||||
self.descendants = descendants
|
||||
}
|
||||
|
||||
init(filter: SpaceFilter) {
|
||||
room = SpaceServiceRoom(spaceRoom: filter.spaceRoom)
|
||||
level = UInt(max(filter.level, 0))
|
||||
descendants = Set(filter.descendants)
|
||||
}
|
||||
|
||||
// Same rooms might appear on multiple levels
|
||||
var id: String {
|
||||
room.id + "\(level)"
|
||||
}
|
||||
|
||||
static func == (lhs: SpaceServiceFilter, rhs: SpaceServiceFilter) -> Bool {
|
||||
lhs.room.id == rhs.room.id
|
||||
}
|
||||
}
|
||||
|
||||
// sourcery: AutoMockable
|
||||
protocol SpaceServiceProxyProtocol {
|
||||
var topLevelSpacesPublisher: CurrentValuePublisher<[SpaceServiceRoomProtocol], Never> { get }
|
||||
var spaceFilterPublisher: CurrentValuePublisher<[SpaceServiceFilter], Never> { get }
|
||||
|
||||
func spaceRoomList(spaceID: String) async -> Result<SpaceRoomListProxyProtocol, SpaceServiceProxyError>
|
||||
/// Returns a joined space given its identifier
|
||||
|
||||
@@ -130,6 +130,20 @@ extension PreviewTests {
|
||||
}
|
||||
}
|
||||
|
||||
func testChatsSpaceFilterCell() async throws {
|
||||
AppSettings.resetAllSettings() // Ensure this test's previews start with fresh settings.
|
||||
for (index, preview) in ChatsSpaceFilterCell_Previews._allPreviews.enumerated() {
|
||||
try await assertSnapshots(matching: preview, step: index)
|
||||
}
|
||||
}
|
||||
|
||||
func testChatsSpaceFiltersScreen() async throws {
|
||||
AppSettings.resetAllSettings() // Ensure this test's previews start with fresh settings.
|
||||
for (index, preview) in ChatsSpaceFiltersScreen_Previews._allPreviews.enumerated() {
|
||||
try await assertSnapshots(matching: preview, step: index)
|
||||
}
|
||||
}
|
||||
|
||||
func testCollapsibleRoomTimelineView() async throws {
|
||||
AppSettings.resetAllSettings() // Ensure this test's previews start with fresh settings.
|
||||
for (index, preview) in CollapsibleRoomTimelineView_Previews._allPreviews.enumerated() {
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3d3f226d34b1e0bd2297f4359189c8b33223316ea7dfb685bc6de866bb58e5ff
|
||||
size 142344
|
||||
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:3d3f226d34b1e0bd2297f4359189c8b33223316ea7dfb685bc6de866bb58e5ff
|
||||
size 142344
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d8abbfad872d996a2f82668373b92f0f33ec51b637263deb14c655d0cb958606
|
||||
size 93320
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:d8abbfad872d996a2f82668373b92f0f33ec51b637263deb14c655d0cb958606
|
||||
size 93320
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e4bb60e28bcde15e69c627fa4b5f6dca2b26a3f25cbf1afb62a9302d6d549b46
|
||||
size 150715
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e4bb60e28bcde15e69c627fa4b5f6dca2b26a3f25cbf1afb62a9302d6d549b46
|
||||
size 150715
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e28fb24e8a9087f8e951ed8c18ab9048cfacd72fcb2c328ddf3e46169306a1b6
|
||||
size 100972
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e28fb24e8a9087f8e951ed8c18ab9048cfacd72fcb2c328ddf3e46169306a1b6
|
||||
size 100972
|
||||
@@ -17125,6 +17125,52 @@ open class RoomSDKMock: MatrixRustSDK.Room, @unchecked Sendable {
|
||||
try await setNameNameClosure?(name)
|
||||
}
|
||||
|
||||
//MARK: - setOwnMemberDisplayName
|
||||
|
||||
open var setOwnMemberDisplayNameDisplayNameThrowableError: Error?
|
||||
open var setOwnMemberDisplayNameDisplayNameUnderlyingCallsCount = 0
|
||||
open var setOwnMemberDisplayNameDisplayNameCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return setOwnMemberDisplayNameDisplayNameUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = setOwnMemberDisplayNameDisplayNameUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
setOwnMemberDisplayNameDisplayNameUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
setOwnMemberDisplayNameDisplayNameUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var setOwnMemberDisplayNameDisplayNameCalled: Bool {
|
||||
return setOwnMemberDisplayNameDisplayNameCallsCount > 0
|
||||
}
|
||||
open var setOwnMemberDisplayNameDisplayNameReceivedDisplayName: String?
|
||||
open var setOwnMemberDisplayNameDisplayNameReceivedInvocations: [String?] = []
|
||||
open var setOwnMemberDisplayNameDisplayNameClosure: ((String?) async throws -> Void)?
|
||||
|
||||
open override func setOwnMemberDisplayName(displayName: String?) async throws {
|
||||
if let error = setOwnMemberDisplayNameDisplayNameThrowableError {
|
||||
throw error
|
||||
}
|
||||
setOwnMemberDisplayNameDisplayNameCallsCount += 1
|
||||
setOwnMemberDisplayNameDisplayNameReceivedDisplayName = displayName
|
||||
DispatchQueue.main.async {
|
||||
self.setOwnMemberDisplayNameDisplayNameReceivedInvocations.append(displayName)
|
||||
}
|
||||
try await setOwnMemberDisplayNameDisplayNameClosure?(displayName)
|
||||
}
|
||||
|
||||
//MARK: - setThreadSubscription
|
||||
|
||||
open var setThreadSubscriptionThreadRootEventIdSubscribedThrowableError: Error?
|
||||
@@ -23601,6 +23647,71 @@ open class SpaceServiceSDKMock: MatrixRustSDK.SpaceService, @unchecked Sendable
|
||||
try await removeChildFromSpaceChildIdSpaceIdClosure?(childId, spaceId)
|
||||
}
|
||||
|
||||
//MARK: - spaceFilters
|
||||
|
||||
open var spaceFiltersUnderlyingCallsCount = 0
|
||||
open var spaceFiltersCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return spaceFiltersUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = spaceFiltersUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
spaceFiltersUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
spaceFiltersUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var spaceFiltersCalled: Bool {
|
||||
return spaceFiltersCallsCount > 0
|
||||
}
|
||||
|
||||
open var spaceFiltersUnderlyingReturnValue: [SpaceFilter]!
|
||||
open var spaceFiltersReturnValue: [SpaceFilter]! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return spaceFiltersUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: [SpaceFilter]? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = spaceFiltersUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
spaceFiltersUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
spaceFiltersUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var spaceFiltersClosure: (() async -> [SpaceFilter])?
|
||||
|
||||
open override func spaceFilters() async -> [SpaceFilter] {
|
||||
spaceFiltersCallsCount += 1
|
||||
if let spaceFiltersClosure = spaceFiltersClosure {
|
||||
return await spaceFiltersClosure()
|
||||
} else {
|
||||
return spaceFiltersReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - spaceRoomList
|
||||
|
||||
open var spaceRoomListSpaceIdThrowableError: Error?
|
||||
@@ -23676,6 +23787,77 @@ open class SpaceServiceSDKMock: MatrixRustSDK.SpaceService, @unchecked Sendable
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - subscribeToSpaceFilters
|
||||
|
||||
open var subscribeToSpaceFiltersListenerUnderlyingCallsCount = 0
|
||||
open var subscribeToSpaceFiltersListenerCallsCount: Int {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return subscribeToSpaceFiltersListenerUnderlyingCallsCount
|
||||
} else {
|
||||
var returnValue: Int? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = subscribeToSpaceFiltersListenerUnderlyingCallsCount
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
subscribeToSpaceFiltersListenerUnderlyingCallsCount = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
subscribeToSpaceFiltersListenerUnderlyingCallsCount = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var subscribeToSpaceFiltersListenerCalled: Bool {
|
||||
return subscribeToSpaceFiltersListenerCallsCount > 0
|
||||
}
|
||||
open var subscribeToSpaceFiltersListenerReceivedListener: SpaceServiceSpaceFiltersListener?
|
||||
open var subscribeToSpaceFiltersListenerReceivedInvocations: [SpaceServiceSpaceFiltersListener] = []
|
||||
|
||||
open var subscribeToSpaceFiltersListenerUnderlyingReturnValue: TaskHandle!
|
||||
open var subscribeToSpaceFiltersListenerReturnValue: TaskHandle! {
|
||||
get {
|
||||
if Thread.isMainThread {
|
||||
return subscribeToSpaceFiltersListenerUnderlyingReturnValue
|
||||
} else {
|
||||
var returnValue: TaskHandle? = nil
|
||||
DispatchQueue.main.sync {
|
||||
returnValue = subscribeToSpaceFiltersListenerUnderlyingReturnValue
|
||||
}
|
||||
|
||||
return returnValue!
|
||||
}
|
||||
}
|
||||
set {
|
||||
if Thread.isMainThread {
|
||||
subscribeToSpaceFiltersListenerUnderlyingReturnValue = newValue
|
||||
} else {
|
||||
DispatchQueue.main.sync {
|
||||
subscribeToSpaceFiltersListenerUnderlyingReturnValue = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
open var subscribeToSpaceFiltersListenerClosure: ((SpaceServiceSpaceFiltersListener) async -> TaskHandle)?
|
||||
|
||||
open override func subscribeToSpaceFilters(listener: SpaceServiceSpaceFiltersListener) async -> TaskHandle {
|
||||
subscribeToSpaceFiltersListenerCallsCount += 1
|
||||
subscribeToSpaceFiltersListenerReceivedListener = listener
|
||||
DispatchQueue.main.async {
|
||||
self.subscribeToSpaceFiltersListenerReceivedInvocations.append(listener)
|
||||
}
|
||||
if let subscribeToSpaceFiltersListenerClosure = subscribeToSpaceFiltersListenerClosure {
|
||||
return await subscribeToSpaceFiltersListenerClosure(listener)
|
||||
} else {
|
||||
return subscribeToSpaceFiltersListenerReturnValue
|
||||
}
|
||||
}
|
||||
|
||||
//MARK: - subscribeToTopLevelJoinedSpaces
|
||||
|
||||
open var subscribeToTopLevelJoinedSpacesListenerUnderlyingCallsCount = 0
|
||||
|
||||
@@ -82,6 +82,25 @@ final class RoomSummaryProviderTests: XCTestCase {
|
||||
.all(filters: [.all(filters: [.category(expect: .group), .joined])] + baseFilters + [.nonLowPriority]))
|
||||
}
|
||||
|
||||
func testRoomIdentifierFilters() async {
|
||||
setupProvider()
|
||||
await Task.yield()
|
||||
|
||||
// Then it should have the default Rust filters enabled.
|
||||
XCTAssertEqual(dynamicEntriesController.setFilterKindCallsCount, 1)
|
||||
XCTAssertEqual(dynamicEntriesController.setFilterKindReceivedInvocations.last,
|
||||
.all(filters: baseFilters))
|
||||
|
||||
// When setting one our user filters.
|
||||
roomSummaryProvider.setFilter(.rooms(roomsIDs: ["SomeRoom"], filters: [.favourites]))
|
||||
await Task.yield()
|
||||
|
||||
// Then that filter should be added to the default Rust filters.
|
||||
XCTAssertEqual(dynamicEntriesController.setFilterKindCallsCount, 2)
|
||||
XCTAssertEqual(dynamicEntriesController.setFilterKindReceivedInvocations.last,
|
||||
.all(filters: [.all(filters: [.favourite, .joined])] + baseFilters + [.identifiers(identifiers: ["SomeRoom"])]))
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func setupProvider(isLowPriorityFilterEnabled: Bool = false) {
|
||||
|
||||
Reference in New Issue
Block a user