Last Owner should edit admins, and not leave when is last (#4372)

This commit is contained in:
Mauro
2025-08-07 10:40:14 +02:00
committed by GitHub
parent be525e6a60
commit 5ab6f90863
67 changed files with 691 additions and 363 deletions
@@ -17,12 +17,16 @@ enum RoomRolesAndPermissionsScreenViewModelAction {
}
struct RoomRolesAndPermissionsScreenViewState: BindableState {
var ownRole: RoomRole
var administratorsAndOwnersCount: Int?
/// The number of administrators in the room.
var administratorCount: Int?
/// The number of moderators in the room.
var moderatorCount: Int?
/// The permissions of the room when loaded.
var permissions: RoomPermissions?
var bindings = RoomRolesAndPermissionsScreenViewStateBindings()
}
@@ -46,7 +50,7 @@ enum RoomRolesAndPermissionsScreenViewAction {
case reset
}
enum RoomRolesAndPermissionsScreenRole {
enum RoomRolesAndPermissionsScreenRole: Hashable {
case administrators
case moderators
}
@@ -14,6 +14,7 @@ class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewM
private let roomProxy: JoinedRoomProxyProtocol
private let userIndicatorController: UserIndicatorControllerProtocol
private let analytics: AnalyticsService
private var ownUser: RoomMemberDetails?
private var actionsSubject: PassthroughSubject<RoomRolesAndPermissionsScreenViewModelAction, Never> = .init()
var actionsPublisher: AnyPublisher<RoomRolesAndPermissionsScreenViewModelAction, Never> {
@@ -24,7 +25,8 @@ class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewM
self.roomProxy = roomProxy
self.userIndicatorController = userIndicatorController
self.analytics = analytics
super.init(initialViewState: RoomRolesAndPermissionsScreenViewState(permissions: initialPermissions))
super.init(initialViewState: RoomRolesAndPermissionsScreenViewState(ownRole: roomProxy.membersPublisher.value.first(where: { $0.userID == roomProxy.ownUserID })?.role ?? .administrator,
permissions: initialPermissions))
// Automatically update the admin/moderator counts.
roomProxy.membersPublisher
@@ -80,16 +82,19 @@ class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewM
secondaryButton: .init(title: L10n.actionCancel, role: .cancel) { })
}
}
// MARK: - Members
private func updateMembers(_ members: [RoomMemberProxyProtocol]) {
// TODO: Will probably be changed when we implement the owners list
state.administratorCount = members.filter { $0.role.isAdminOrHigher && $0.isActive }.count
state.administratorsAndOwnersCount = members.filter { $0.role.isAdminOrHigher && $0.isActive }.count
state.administratorCount = members.filter { $0.role == .administrator && $0.isActive }.count
state.moderatorCount = members.filter { $0.role == .moderator && $0.isActive }.count
if let ownUser = members.first(where: { $0.userID == roomProxy.ownUserID }) {
state.ownRole = ownUser.role
}
}
private func updateOwnRole(_ role: RoomMemberDetails.Role) async {
private func updateOwnRole(_ role: RoomRole) async {
showSavingIndicator()
// A task we can await until the room's info gets modified with the new power levels.
@@ -142,7 +147,7 @@ class RoomRolesAndPermissionsScreenViewModel: RoomRolesAndPermissionsScreenViewM
hideSavingIndicator()
}
// MARK: Loading indicator
private static let savingIndicatorID = "RolesAndPermissionsSaving"
@@ -26,13 +26,23 @@ struct RoomRolesAndPermissionsScreen: View {
private var rolesSection: some View {
Section {
ListRow(label: .default(title: L10n.screenRoomRolesAndPermissionsAdmins,
icon: \.admin),
details: administratorDetails,
kind: .navigationLink {
context.send(viewAction: .editRoles(.administrators))
})
.accessibilityIdentifier(A11yIdentifiers.roomRolesAndPermissionsScreen.administrators)
if context.viewState.ownRole == .creator {
ListRow(label: .default(title: L10n.screenRoomRolesAndPermissionsAdminsAndOwners,
icon: \.admin),
details: administratorOrOwnersDetails,
kind: .navigationLink {
context.send(viewAction: .editRoles(.administrators))
})
.accessibilityIdentifier(A11yIdentifiers.roomRolesAndPermissionsScreen.administrators)
} else {
ListRow(label: .default(title: L10n.screenRoomRolesAndPermissionsAdmins,
icon: \.admin),
details: administratorDetails,
kind: .navigationLink {
context.send(viewAction: .editRoles(.administrators))
})
.accessibilityIdentifier(A11yIdentifiers.roomRolesAndPermissionsScreen.administrators)
}
ListRow(label: .default(title: L10n.screenRoomRolesAndPermissionsModerators,
icon: \.chatProblem),
@@ -42,17 +52,27 @@ struct RoomRolesAndPermissionsScreen: View {
})
.accessibilityIdentifier(A11yIdentifiers.roomRolesAndPermissionsScreen.moderators)
ListRow(label: .default(title: L10n.screenRoomRolesAndPermissionsChangeMyRole,
icon: \.edit),
kind: .button {
context.send(viewAction: .editOwnUserRole)
})
if context.viewState.ownRole != .creator {
ListRow(label: .default(title: L10n.screenRoomRolesAndPermissionsChangeMyRole,
icon: \.edit),
kind: .button {
context.send(viewAction: .editOwnUserRole)
})
}
} header: {
Text(L10n.screenRoomRolesAndPermissionsRolesHeader)
.compoundListSectionHeader()
}
}
private var administratorOrOwnersDetails: ListRowDetails<Image> {
if let administratorCount = context.viewState.administratorsAndOwnersCount {
.title("\(administratorCount)")
} else {
.isWaiting(true)
}
}
private var administratorDetails: ListRowDetails<Image> {
if let administratorCount = context.viewState.administratorCount {
.title("\(administratorCount)")
@@ -121,9 +141,20 @@ struct RoomRolesAndPermissionsScreen_Previews: PreviewProvider, TestablePreview
roomProxy: JoinedRoomProxyMock(.init(members: .allMembersAsAdmin)),
userIndicatorController: UserIndicatorControllerMock(),
analytics: ServiceLocator.shared.analytics)
static let creatorViewModel = RoomRolesAndPermissionsScreenViewModel(initialPermissions: RoomPermissions(powerLevels: .mock),
roomProxy: JoinedRoomProxyMock(.init(members: .allMembersAsCreator)),
userIndicatorController: UserIndicatorControllerMock(),
analytics: ServiceLocator.shared.analytics)
static var previews: some View {
NavigationStack {
RoomRolesAndPermissionsScreen(context: viewModel.context)
}
.previewDisplayName("Admin")
NavigationStack {
RoomRolesAndPermissionsScreen(context: creatorViewModel.context)
}
.previewDisplayName("Creator")
}
}