mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Fix various bugs in the moderation feature. (#2608)
* Fix search field string. * Show your own user as an Admin when changing roles. * Also show invited users when changing roles. * Don't allow admin's to kick/ban other admins or themselves. * Fix a bug when left members were counted as admins/moderators. * Show when a member is pending. * Add sections to the change role screen.
This commit is contained in:
@@ -19,6 +19,15 @@ import Foundation
|
||||
enum RoomMembersListScreenViewModelAction {
|
||||
case selectMember(_ member: RoomMemberProxyProtocol)
|
||||
case invite
|
||||
|
||||
var isSelectMember: Bool {
|
||||
switch self {
|
||||
case .selectMember:
|
||||
true
|
||||
default:
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The different modes that the screen can be in.
|
||||
@@ -54,7 +63,7 @@ struct RoomMembersListScreenViewState: BindableState {
|
||||
self.bannedMembers = bannedMembers
|
||||
self.bindings = bindings
|
||||
}
|
||||
|
||||
|
||||
var visibleJoinedMembers: [RoomMemberDetails] {
|
||||
joinedMembers
|
||||
.filter { $0.matches(searchQuery: bindings.searchQuery) }
|
||||
@@ -76,12 +85,25 @@ struct RoomMembersListScreenViewStateBindings {
|
||||
/// The current mode the screen is in.
|
||||
var mode: RoomMembersListScreenMode = .members
|
||||
/// A selected member to kick, ban, promote etc.
|
||||
var memberToManage: RoomMemberDetails?
|
||||
var memberToManage: RoomMembersListScreenManagementDetails?
|
||||
|
||||
/// Information describing the currently displayed alert.
|
||||
var alertInfo: AlertInfo<RoomMembersListScreenAlertType>?
|
||||
}
|
||||
|
||||
/// Information about managing a particular room member.
|
||||
struct RoomMembersListScreenManagementDetails: Identifiable {
|
||||
var id: String { member.id }
|
||||
|
||||
/// The member that is being managed.
|
||||
let member: RoomMemberDetails
|
||||
|
||||
/// A management action that can be performed on the member.
|
||||
enum Action { case kick, ban }
|
||||
/// The management actions available for `member`.
|
||||
let actions: [Action]
|
||||
}
|
||||
|
||||
enum RoomMembersListScreenViewAction {
|
||||
case selectMember(RoomMemberDetails)
|
||||
case showMemberDetails(RoomMemberDetails)
|
||||
@@ -94,13 +116,3 @@ enum RoomMembersListScreenViewAction {
|
||||
enum RoomMembersListScreenAlertType: Hashable {
|
||||
case unbanConfirmation(RoomMemberDetails)
|
||||
}
|
||||
|
||||
private extension RoomMemberDetails {
|
||||
func matches(searchQuery: String) -> Bool {
|
||||
guard !searchQuery.isEmpty else {
|
||||
return true
|
||||
}
|
||||
|
||||
return id.localizedCaseInsensitiveContains(searchQuery) || name?.localizedCaseInsensitiveContains(searchQuery) ?? false
|
||||
}
|
||||
}
|
||||
|
||||
+26
-12
@@ -149,18 +149,32 @@ class RoomMembersListScreenViewModel: RoomMembersListScreenViewModelType, RoomMe
|
||||
}
|
||||
|
||||
private func selectMember(_ member: RoomMemberDetails) {
|
||||
if appSettings.roomModerationEnabled, state.canKickUsers || state.canBanUsers {
|
||||
if member.isBanned {
|
||||
state.bindings.alertInfo = AlertInfo(id: .unbanConfirmation(member),
|
||||
title: L10n.screenRoomMemberListManageMemberUnbanTitle,
|
||||
message: L10n.screenRoomMemberListManageMemberUnbanMessage,
|
||||
primaryButton: .init(title: L10n.screenRoomMemberListManageMemberUnbanAction) { [weak self] in
|
||||
self?.context.send(viewAction: .unbanMember(member))
|
||||
},
|
||||
secondaryButton: .init(title: L10n.actionCancel, role: .cancel) { })
|
||||
} else {
|
||||
state.bindings.memberToManage = member
|
||||
}
|
||||
guard appSettings.roomModerationEnabled else {
|
||||
showMemberDetails(member)
|
||||
return
|
||||
}
|
||||
|
||||
if member.isBanned { // No need to check canBan here, banned users are only shown when it is true.
|
||||
state.bindings.alertInfo = AlertInfo(id: .unbanConfirmation(member),
|
||||
title: L10n.screenRoomMemberListManageMemberUnbanTitle,
|
||||
message: L10n.screenRoomMemberListManageMemberUnbanMessage,
|
||||
primaryButton: .init(title: L10n.screenRoomMemberListManageMemberUnbanAction) { [weak self] in
|
||||
self?.context.send(viewAction: .unbanMember(member))
|
||||
},
|
||||
secondaryButton: .init(title: L10n.actionCancel, role: .cancel) { })
|
||||
return
|
||||
}
|
||||
|
||||
var actions = [RoomMembersListScreenManagementDetails.Action]()
|
||||
if state.canKickUsers, member.role != .administrator {
|
||||
actions.append(.kick)
|
||||
}
|
||||
if state.canBanUsers, member.role != .administrator {
|
||||
actions.append(.ban)
|
||||
}
|
||||
|
||||
if !actions.isEmpty {
|
||||
state.bindings.memberToManage = .init(member: member, actions: actions)
|
||||
} else {
|
||||
showMemberDetails(member)
|
||||
}
|
||||
|
||||
+7
-2
@@ -19,6 +19,8 @@ import SwiftUI
|
||||
|
||||
struct RoomMembersListManageMemberSheet: View {
|
||||
let member: RoomMemberDetails
|
||||
let actions: [RoomMembersListScreenManagementDetails.Action]
|
||||
|
||||
@ObservedObject var context: RoomMembersListScreenViewModel.Context
|
||||
|
||||
@State private var isPresentingBanConfirmation = false
|
||||
@@ -38,7 +40,7 @@ struct RoomMembersListManageMemberSheet: View {
|
||||
context.send(viewAction: .showMemberDetails(member))
|
||||
})
|
||||
|
||||
if context.viewState.canKickUsers, !member.isBanned {
|
||||
if actions.contains(.kick) {
|
||||
ListRow(label: .default(title: L10n.screenRoomMemberListManageMemberRemove,
|
||||
icon: \.close),
|
||||
kind: .button {
|
||||
@@ -46,7 +48,7 @@ struct RoomMembersListManageMemberSheet: View {
|
||||
})
|
||||
}
|
||||
|
||||
if context.viewState.canBanUsers, !member.isBanned {
|
||||
if actions.contains(.ban) {
|
||||
ListRow(label: .default(title: L10n.screenRoomMemberListManageMemberBan,
|
||||
icon: \.block,
|
||||
role: .destructive),
|
||||
@@ -76,11 +78,13 @@ struct RoomMembersListManageMemberSheet_Previews: PreviewProvider, TestablePrevi
|
||||
|
||||
static var previews: some View {
|
||||
RoomMembersListManageMemberSheet(member: .init(withProxy: RoomMemberProxyMock.mockDan),
|
||||
actions: [.kick, .ban],
|
||||
context: viewModel.context)
|
||||
.previewDisplayName("Joined")
|
||||
.snapshot(delay: 0.2)
|
||||
|
||||
RoomMembersListManageMemberSheet(member: .init(withProxy: RoomMemberProxyMock.mockBanned[3]),
|
||||
actions: [],
|
||||
context: viewModel.context)
|
||||
.previewDisplayName("Banned")
|
||||
.snapshot(delay: 0.2)
|
||||
@@ -94,6 +98,7 @@ struct RoomMembersListManageMemberSheetLive_Previews: PreviewProvider {
|
||||
Color.clear
|
||||
.sheet(isPresented: .constant(true)) {
|
||||
RoomMembersListManageMemberSheet(member: .init(withProxy: RoomMemberProxyMock.mockDan),
|
||||
actions: [.kick, .ban],
|
||||
context: viewModel.context)
|
||||
}
|
||||
.previewDisplayName("Sheet")
|
||||
|
||||
@@ -49,13 +49,15 @@ struct RoomMembersListScreen: View {
|
||||
.background(.compound.bgCanvasDefault)
|
||||
}
|
||||
}
|
||||
.searchable(text: $context.searchQuery, placement: .navigationBarDrawer(displayMode: .always))
|
||||
.searchable(text: $context.searchQuery,
|
||||
placement: .navigationBarDrawer(displayMode: .always),
|
||||
prompt: L10n.commonSearchForSomeone)
|
||||
.compoundSearchField()
|
||||
.autocorrectionDisabled()
|
||||
.background(Color.compound.bgCanvasDefault.ignoresSafeArea())
|
||||
.navigationTitle(L10n.commonPeople)
|
||||
.sheet(item: $context.memberToManage) {
|
||||
RoomMembersListManageMemberSheet(member: $0, context: context)
|
||||
RoomMembersListManageMemberSheet(member: $0.member, actions: $0.actions, context: context)
|
||||
}
|
||||
.alert(item: $context.alertInfo)
|
||||
.toolbar { toolbar }
|
||||
|
||||
Reference in New Issue
Block a user