Skip to content

Commit

Permalink
Kick/ban room members (#2501)
Browse files Browse the repository at this point in the history
* Add a temporary membershipChangePublisher on the timeline.

* Kick/Ban users from a room.

* Unit tests.

* Disable autocorrection on the members list search field.
  • Loading branch information
pixlwave authored Feb 27, 2024
1 parent 1afde10 commit fdbaef5
Show file tree
Hide file tree
Showing 30 changed files with 561 additions and 43 deletions.
4 changes: 4 additions & 0 deletions ElementX.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,7 @@
5D4643E485C179B2F485C519 /* MentionSuggestionItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2FD0E68C42CA7DDCD4CAD68D /* MentionSuggestionItemView.swift */; };
5D53AE9342A4C06B704247ED /* MediaLoaderProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1A02406480C351B8C6E0682C /* MediaLoaderProtocol.swift */; };
5D70FAE4D2BF4553AFFFFE41 /* NotificationItemProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 25F7FE40EF7490A7E09D7BE6 /* NotificationItemProxy.swift */; };
5DD0EF30070DC0A82C5CCD33 /* RoomMembersListManageMemberSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = FC853F9B4FBE039D2C16EC6B /* RoomMembersListManageMemberSheet.swift */; };
5DD85A0FE3D85AEC3C7EFE36 /* DeveloperOptionsScreenCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7C7CFA6B2A62A685FF6CE3 /* DeveloperOptionsScreenCoordinator.swift */; };
5E0F2E612718BB4397A6D40A /* TextRoomTimelineView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F9E785D5137510481733A3E8 /* TextRoomTimelineView.swift */; };
5EE1D4E316D66943E97FDCF2 /* BloomView.swift in Sources */ = {isa = PBXBuildFile; fileRef = D7BEB970F500BFB248443FA1 /* BloomView.swift */; };
Expand Down Expand Up @@ -2008,6 +2009,7 @@
FBB0328F2887BF0A65BC5D49 /* NotificationSettingsEditScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationSettingsEditScreen.swift; sourceTree = "<group>"; };
FBC776F301D374A3298C69DA /* AppCoordinatorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppCoordinatorProtocol.swift; sourceTree = "<group>"; };
FC2D505742FDA21FCDC4C18A /* AudioRoomTimelineView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioRoomTimelineView.swift; sourceTree = "<group>"; };
FC853F9B4FBE039D2C16EC6B /* RoomMembersListManageMemberSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RoomMembersListManageMemberSheet.swift; sourceTree = "<group>"; };
FCE93F0CBF0D96B77111C413 /* AppLockFlowCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppLockFlowCoordinator.swift; sourceTree = "<group>"; };
FD1275D9CE0FFBA6E8E85426 /* UserIndicatorController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserIndicatorController.swift; sourceTree = "<group>"; };
FDB9C37196A4C79F24CE80C6 /* KeychainControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainControllerTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3817,6 +3819,7 @@
949B06577E5265373013DDAB /* View */ = {
isa = PBXGroup;
children = (
FC853F9B4FBE039D2C16EC6B /* RoomMembersListManageMemberSheet.swift */,
1B8E176484A89BAC389D4076 /* RoomMembersListScreen.swift */,
CC03209FDE8CE0810617BFFF /* RoomMembersListScreenMemberCell.swift */,
);
Expand Down Expand Up @@ -5882,6 +5885,7 @@
6448F8D1D3CA4CD27BB4CADD /* RoomMemberProxy.swift in Sources */,
92D9088B901CEBB1A99ECA4E /* RoomMemberProxyMock.swift in Sources */,
F118DD449066E594F63C697D /* RoomMemberProxyProtocol.swift in Sources */,
5DD0EF30070DC0A82C5CCD33 /* RoomMembersListManageMemberSheet.swift in Sources */,
C08AAE7563E0722C9383F51C /* RoomMembersListScreen.swift in Sources */,
1C9BB74711E5F24C77B7FED0 /* RoomMembersListScreenCoordinator.swift in Sources */,
A975D60EA49F6AF73308809F /* RoomMembersListScreenMemberCell.swift in Sources */,
Expand Down
23 changes: 16 additions & 7 deletions ElementX.xcodeproj/xcshareddata/xcschemes/PreviewTests.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
version = "1.7">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
buildImplicitDependencies = "YES"
runPostActionsOnFailure = "NO">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
Expand All @@ -29,6 +30,12 @@
shouldUseLaunchSchemeArgsEnv = "YES"
codeCoverageEnabled = "YES"
onlyGenerateCoverageForSpecifiedTargets = "YES">
<TestPlans>
<TestPlanReference
default = "YES"
reference = "container:PreviewTests/SupportingFiles/PreviewTests.xctestplan">
</TestPlanReference>
</TestPlans>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
Expand All @@ -38,6 +45,10 @@
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
</Testables>
<CommandLineArguments>
</CommandLineArguments>
<CodeCoverageTargets>
<BuildableReference
BuildableIdentifier = "primary"
Expand All @@ -47,12 +58,6 @@
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</CodeCoverageTargets>
<TestPlans>
<TestPlanReference
reference = "container:PreviewTests/SupportingFiles/PreviewTests.xctestplan"
default = "YES">
</TestPlanReference>
</TestPlans>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
Expand All @@ -73,13 +78,17 @@
ReferencedContainer = "container:ElementX.xcodeproj">
</BuildableReference>
</MacroExpansion>
<CommandLineArguments>
</CommandLineArguments>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<CommandLineArguments>
</CommandLineArguments>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
Expand Down
16 changes: 15 additions & 1 deletion ElementX/Resources/Localizations/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -523,11 +523,14 @@
"screen_room_member_details_unblock_alert_action" = "Unblock";
"screen_room_member_details_unblock_alert_description" = "You'll be able to see all messages from them again.";
"screen_room_member_details_unblock_user" = "Unblock user";
"screen_room_member_list_banning_user" = "Banning %1$@";
"screen_room_member_list_manage_member_remove" = "Remove member";
"screen_room_member_list_manage_member_remove_confirmation_ban" = "Remove and ban member";
"screen_room_member_list_manage_member_remove_confirmation_kick" = "Only remove member";
"screen_room_member_list_manage_member_remove_confirmation_title" = "Remove member and ban from joining in the future?";
"screen_room_member_list_manage_member_unban" = "Unban";
"screen_room_member_list_manage_member_unban_action" = "Unban";
"screen_room_member_list_manage_member_unban_message" = "They will be able to join this room again if invited.";
"screen_room_member_list_manage_member_unban_title" = "Unban user";
"screen_room_member_list_manage_member_user_info" = "See user info";
"screen_room_member_list_mode_banned" = "Banned";
"screen_room_member_list_mode_members" = "Members";
Expand All @@ -536,6 +539,7 @@
"screen_room_member_list_role_administrator" = "Admin";
"screen_room_member_list_role_moderator" = "Moderator";
"screen_room_member_list_room_members_header_title" = "Room members";
"screen_room_member_list_unbanning_user" = "Unbanning %1$@";
"screen_room_message_copied" = "Message copied";
"screen_room_no_permission_to_post" = "You do not have permission to post to this room";
"screen_room_notification_settings_allow_custom" = "Allow custom setting";
Expand All @@ -555,6 +559,15 @@
"screen_room_reactions_show_more" = "Show more";
"screen_room_retry_send_menu_send_again_action" = "Send again";
"screen_room_retry_send_menu_title" = "Your message failed to send";
"screen_room_roles_and_permissions_admins" = "Admins";
"screen_room_roles_and_permissions_member_moderation" = "Member moderation";
"screen_room_roles_and_permissions_messages_and_content" = "Messages and content";
"screen_room_roles_and_permissions_moderators" = "Moderators";
"screen_room_roles_and_permissions_permissions_header" = "Permissions";
"screen_room_roles_and_permissions_reset" = "Reset roles and permissions";
"screen_room_roles_and_permissions_roles_header" = "Roles";
"screen_room_roles_and_permissions_room_details" = "Room details";
"screen_room_roles_and_permissions_title" = "Roles and permissions";
"screen_room_timeline_add_reaction" = "Add emoji";
"screen_room_timeline_less_reactions" = "Show less";
"screen_room_typing_many_members_first_component_ios" = "%1$@, %2$@ and ";
Expand Down Expand Up @@ -726,6 +739,7 @@
"screen_room_details_invite_people_title" = "Invite people";
"screen_room_details_leave_conversation_title" = "Leave conversation";
"screen_room_details_leave_room_title" = "Leave room";
"screen_room_details_roles_and_permissions" = "Roles and permissions";
"screen_room_details_room_name_label" = "Room name";
"screen_room_details_security_title" = "Security";
"screen_room_details_topic_title" = "Topic";
Expand Down
4 changes: 4 additions & 0 deletions ElementX/Sources/Application/AppSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ final class AppSettings {
case roomListFiltersEnabled
case markAsUnreadEnabled
case markAsFavouriteEnabled
case roomModerationEnabled
}

private static var suiteName: String = InfoPlistReader.main.appGroupIdentifier
Expand Down Expand Up @@ -289,6 +290,9 @@ final class AppSettings {
@UserPreference(key: UserDefaultsKeys.markAsFavouriteEnabled, defaultValue: false, storageType: .userDefaults(store))
var markAsFavouriteEnabled

@UserPreference(key: UserDefaultsKeys.roomModerationEnabled, defaultValue: false, storageType: .userDefaults(store))
var roomModerationEnabled

#endif

// MARK: - Shared
Expand Down
3 changes: 2 additions & 1 deletion ElementX/Sources/FlowCoordinators/RoomFlowCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,8 @@ class RoomFlowCoordinator: FlowCoordinatorProtocol {
}

let params = RoomMembersListScreenCoordinatorParameters(mediaProvider: userSession.mediaProvider,
roomProxy: roomProxy)
roomProxy: roomProxy,
appSettings: appSettings)
let coordinator = RoomMembersListScreenCoordinator(parameters: params)

coordinator.actions
Expand Down
34 changes: 33 additions & 1 deletion ElementX/Sources/Generated/Strings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1249,6 +1249,8 @@ internal enum L10n {
internal static var screenRoomDetailsNotificationModeDefault: String { return L10n.tr("Localizable", "screen_room_details_notification_mode_default") }
/// Notifications
internal static var screenRoomDetailsNotificationTitle: String { return L10n.tr("Localizable", "screen_room_details_notification_title") }
/// Roles and permissions
internal static var screenRoomDetailsRolesAndPermissions: String { return L10n.tr("Localizable", "screen_room_details_roles_and_permissions") }
/// Room name
internal static var screenRoomDetailsRoomNameLabel: String { return L10n.tr("Localizable", "screen_room_details_room_name_label") }
/// Security
Expand Down Expand Up @@ -1283,6 +1285,10 @@ internal enum L10n {
internal static var screenRoomMemberDetailsUnblockAlertDescription: String { return L10n.tr("Localizable", "screen_room_member_details_unblock_alert_description") }
/// Unblock user
internal static var screenRoomMemberDetailsUnblockUser: String { return L10n.tr("Localizable", "screen_room_member_details_unblock_user") }
/// Banning %1$@
internal static func screenRoomMemberListBanningUser(_ p1: Any) -> String {
return L10n.tr("Localizable", "screen_room_member_list_banning_user", String(describing: p1))
}
/// Plural format key: "%#@COUNT@"
internal static func screenRoomMemberListHeaderTitle(_ p1: Int) -> String {
return L10n.tr("Localizable", "screen_room_member_list_header_title", p1)
Expand All @@ -1296,7 +1302,11 @@ internal enum L10n {
/// Remove member and ban from joining in the future?
internal static var screenRoomMemberListManageMemberRemoveConfirmationTitle: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_remove_confirmation_title") }
/// Unban
internal static var screenRoomMemberListManageMemberUnban: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_unban") }
internal static var screenRoomMemberListManageMemberUnbanAction: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_unban_action") }
/// They will be able to join this room again if invited.
internal static var screenRoomMemberListManageMemberUnbanMessage: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_unban_message") }
/// Unban user
internal static var screenRoomMemberListManageMemberUnbanTitle: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_unban_title") }
/// See user info
internal static var screenRoomMemberListManageMemberUserInfo: String { return L10n.tr("Localizable", "screen_room_member_list_manage_member_user_info") }
/// Banned
Expand All @@ -1315,6 +1325,10 @@ internal enum L10n {
internal static var screenRoomMemberListRoleModerator: String { return L10n.tr("Localizable", "screen_room_member_list_role_moderator") }
/// Room members
internal static var screenRoomMemberListRoomMembersHeaderTitle: String { return L10n.tr("Localizable", "screen_room_member_list_room_members_header_title") }
/// Unbanning %1$@
internal static func screenRoomMemberListUnbanningUser(_ p1: Any) -> String {
return L10n.tr("Localizable", "screen_room_member_list_unbanning_user", String(describing: p1))
}
/// Notify the whole room
internal static var screenRoomMentionsAtRoomSubtitle: String { return L10n.tr("Localizable", "screen_room_mentions_at_room_subtitle") }
/// Everyone
Expand Down Expand Up @@ -1361,6 +1375,24 @@ internal enum L10n {
internal static var screenRoomRetrySendMenuSendAgainAction: String { return L10n.tr("Localizable", "screen_room_retry_send_menu_send_again_action") }
/// Your message failed to send
internal static var screenRoomRetrySendMenuTitle: String { return L10n.tr("Localizable", "screen_room_retry_send_menu_title") }
/// Admins
internal static var screenRoomRolesAndPermissionsAdmins: String { return L10n.tr("Localizable", "screen_room_roles_and_permissions_admins") }
/// Member moderation
internal static var screenRoomRolesAndPermissionsMemberModeration: String { return L10n.tr("Localizable", "screen_room_roles_and_permissions_member_moderation") }
/// Messages and content
internal static var screenRoomRolesAndPermissionsMessagesAndContent: String { return L10n.tr("Localizable", "screen_room_roles_and_permissions_messages_and_content") }
/// Moderators
internal static var screenRoomRolesAndPermissionsModerators: String { return L10n.tr("Localizable", "screen_room_roles_and_permissions_moderators") }
/// Permissions
internal static var screenRoomRolesAndPermissionsPermissionsHeader: String { return L10n.tr("Localizable", "screen_room_roles_and_permissions_permissions_header") }
/// Reset roles and permissions
internal static var screenRoomRolesAndPermissionsReset: String { return L10n.tr("Localizable", "screen_room_roles_and_permissions_reset") }
/// Roles
internal static var screenRoomRolesAndPermissionsRolesHeader: String { return L10n.tr("Localizable", "screen_room_roles_and_permissions_roles_header") }
/// Room details
internal static var screenRoomRolesAndPermissionsRoomDetails: String { return L10n.tr("Localizable", "screen_room_roles_and_permissions_room_details") }
/// Roles and permissions
internal static var screenRoomRolesAndPermissionsTitle: String { return L10n.tr("Localizable", "screen_room_roles_and_permissions_title") }
/// Add emoji
internal static var screenRoomTimelineAddReaction: String { return L10n.tr("Localizable", "screen_room_timeline_add_reaction") }
/// Show less
Expand Down
73 changes: 73 additions & 0 deletions ElementX/Sources/Mocks/Generated/GeneratedMocks.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1750,6 +1750,11 @@ class RoomMemberProxyMock: RoomMemberProxyProtocol {
set(value) { underlyingCanInviteUsers = value }
}
var underlyingCanInviteUsers: Bool!
var canKickUsers: Bool {
get { return underlyingCanKickUsers }
set(value) { underlyingCanKickUsers = value }
}
var underlyingCanKickUsers: Bool!
var canBanUsers: Bool {
get { return underlyingCanBanUsers }
set(value) { underlyingCanBanUsers = value }
Expand Down Expand Up @@ -2338,6 +2343,69 @@ class RoomProxyMock: RoomProxyProtocol {
return flagAsFavouriteReturnValue
}
}
//MARK: - kickUser

var kickUserCallsCount = 0
var kickUserCalled: Bool {
return kickUserCallsCount > 0
}
var kickUserReceivedUserID: String?
var kickUserReceivedInvocations: [String] = []
var kickUserReturnValue: Result<Void, RoomProxyError>!
var kickUserClosure: ((String) async -> Result<Void, RoomProxyError>)?

func kickUser(_ userID: String) async -> Result<Void, RoomProxyError> {
kickUserCallsCount += 1
kickUserReceivedUserID = userID
kickUserReceivedInvocations.append(userID)
if let kickUserClosure = kickUserClosure {
return await kickUserClosure(userID)
} else {
return kickUserReturnValue
}
}
//MARK: - banUser

var banUserCallsCount = 0
var banUserCalled: Bool {
return banUserCallsCount > 0
}
var banUserReceivedUserID: String?
var banUserReceivedInvocations: [String] = []
var banUserReturnValue: Result<Void, RoomProxyError>!
var banUserClosure: ((String) async -> Result<Void, RoomProxyError>)?

func banUser(_ userID: String) async -> Result<Void, RoomProxyError> {
banUserCallsCount += 1
banUserReceivedUserID = userID
banUserReceivedInvocations.append(userID)
if let banUserClosure = banUserClosure {
return await banUserClosure(userID)
} else {
return banUserReturnValue
}
}
//MARK: - unbanUser

var unbanUserCallsCount = 0
var unbanUserCalled: Bool {
return unbanUserCallsCount > 0
}
var unbanUserReceivedUserID: String?
var unbanUserReceivedInvocations: [String] = []
var unbanUserReturnValue: Result<Void, RoomProxyError>!
var unbanUserClosure: ((String) async -> Result<Void, RoomProxyError>)?

func unbanUser(_ userID: String) async -> Result<Void, RoomProxyError> {
unbanUserCallsCount += 1
unbanUserReceivedUserID = userID
unbanUserReceivedInvocations.append(userID)
if let unbanUserClosure = unbanUserClosure {
return await unbanUserClosure(userID)
} else {
return unbanUserReturnValue
}
}
//MARK: - canUserJoinCall

var canUserJoinCallUserIDCallsCount = 0
Expand Down Expand Up @@ -2389,6 +2457,11 @@ class RoomTimelineProviderMock: RoomTimelineProviderProtocol {
set(value) { underlyingBackPaginationState = value }
}
var underlyingBackPaginationState: BackPaginationStatus!
var membershipChangePublisher: AnyPublisher<Void, Never> {
get { return underlyingMembershipChangePublisher }
set(value) { underlyingMembershipChangePublisher = value }
}
var underlyingMembershipChangePublisher: AnyPublisher<Void, Never>!

}
class SecureBackupControllerMock: SecureBackupControllerProtocol {
Expand Down
2 changes: 2 additions & 0 deletions ElementX/Sources/Mocks/RoomMemberProxyMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ struct RoomMemberProxyMockConfiguration {
var powerLevel = 0
var role = RoomMemberRole.user
var canInviteUsers = false
var canKickUsers = false
var canBanUsers = false
var canSendStateEvent: (StateEventType) -> Bool = { _ in true }
}
Expand All @@ -43,6 +44,7 @@ extension RoomMemberProxyMock {
powerLevel = configuration.powerLevel
role = configuration.role
canInviteUsers = configuration.canInviteUsers
canKickUsers = configuration.canKickUsers
canBanUsers = configuration.canBanUsers
canSendStateEventTypeClosure = configuration.canSendStateEvent
}
Expand Down
Loading

0 comments on commit fdbaef5

Please sign in to comment.