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:
Doug
2024-03-27 10:33:40 +00:00
committed by GitHub
parent bb725db6bb
commit e424a02a2b
51 changed files with 413 additions and 148 deletions
@@ -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
}
}
@@ -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)
}
@@ -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 }