Separate priviled members list, and more useful permissions
Having both the member list and permission controls is troublesome, and scales the larger your moderation team is. We eventually may want to manage banned/muted users too so I think it warrants having a new page. I also moved the search field to the top so it's more accessible. As for permissions, I tried to improve the UX generally while not changing it too heavily. First the easy change is to the text, hopefully the sections should be clearer (especially for "state" events.) The bigger change here is the new sections, I tried to make it more useful and organized. Additionally, I added more permissions like sharing live locations and polls so they're more easily configurable. One other change is that permissions are visible regardless of whether you can set them or not, matching Element's behavior.
This commit is contained in:
@@ -33,207 +33,10 @@ FormCard.FormCardPage {
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title", "Privileged Users")
|
||||
visible: !root.loading
|
||||
title: i18nc("@title", "Power Levels")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
visible: !root.loading
|
||||
|
||||
Repeater {
|
||||
id: permissions
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: RoomManager.userListModel
|
||||
sortRoleName: "powerLevel"
|
||||
sortOrder: Qt.DescendingOrder
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
let powerLevelRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), UserListModel.PowerLevelRole);
|
||||
return powerLevelRole != 0;
|
||||
}
|
||||
}
|
||||
delegate: FormCard.FormTextDelegate {
|
||||
id: privilegedUserDelegate
|
||||
required property string userId
|
||||
required property string name
|
||||
required property int powerLevel
|
||||
required property string powerLevelString
|
||||
required property bool isCreator
|
||||
|
||||
text: name
|
||||
textItem.textFormat: Text.PlainText
|
||||
description: userId
|
||||
contentItem.children: RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
QQC2.Label {
|
||||
id: powerLevelLabel
|
||||
text: privilegedUserDelegate.powerLevelString
|
||||
visible: (!root.room.canSendState("m.room.power_levels") || (root.room.memberEffectivePowerLevel(root.room.localMember.id) <= privilegedUserDelegate.powerLevel && privilegedUserDelegate.userId != root.room.localMember.id)) || privilegedUserDelegate.isCreator
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
}
|
||||
QQC2.ComboBox {
|
||||
focusPolicy: Qt.NoFocus // provided by parent
|
||||
model: PowerLevelModel {}
|
||||
textRole: "name"
|
||||
valueRole: "value"
|
||||
visible: !powerLevelLabel.visible
|
||||
Component.onCompleted: {
|
||||
let index = indexOfValue(privilegedUserDelegate.powerLevel)
|
||||
if (index === -1) {
|
||||
displayText = privilegedUserDelegate.powerLevelString;
|
||||
} else {
|
||||
currentIndex = index;
|
||||
}
|
||||
}
|
||||
onActivated: {
|
||||
root.room.setUserPowerLevel(privilegedUserDelegate.userId, currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormDelegateSeparator {
|
||||
below: userListSearchCard
|
||||
}
|
||||
FormCard.AbstractFormDelegate {
|
||||
id: userListSearchCard
|
||||
visible: root.room.canSendState("m.room.power_levels")
|
||||
|
||||
contentItem: Kirigami.SearchField {
|
||||
id: userListSearchField
|
||||
|
||||
autoAccept: false
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
Keys.onUpPressed: userListView.decrementCurrentIndex()
|
||||
Keys.onDownPressed: userListView.incrementCurrentIndex()
|
||||
|
||||
onAccepted: (userListView.itemAtIndex(userListView.currentIndex) as Delegates.RoundedItemDelegate).action.trigger()
|
||||
}
|
||||
QQC2.Popup {
|
||||
id: userListSearchPopup
|
||||
|
||||
x: userListSearchField.x
|
||||
y: userListSearchField.y - height
|
||||
width: userListSearchField.width
|
||||
height: {
|
||||
let maxHeight = userListSearchField.mapToGlobal(userListSearchField.x, userListSearchField.y).y - Kirigami.Units.largeSpacing * 3;
|
||||
let minHeight = Kirigami.Units.gridUnit * 2 + userListSearchPopup.padding * 2;
|
||||
let filterContentHeight = userListView.contentHeight + userListSearchPopup.padding * 2;
|
||||
return Math.max(Math.min(filterContentHeight, maxHeight), minHeight);
|
||||
}
|
||||
padding: Kirigami.Units.smallSpacing
|
||||
leftPadding: Kirigami.Units.smallSpacing / 2
|
||||
rightPadding: Kirigami.Units.smallSpacing / 2
|
||||
modal: false
|
||||
onClosed: userListSearchField.text = ""
|
||||
|
||||
background: Kirigami.ShadowedRectangle {
|
||||
property color borderColor: Kirigami.Theme.textColor
|
||||
|
||||
Kirigami.Theme.colorSet: Kirigami.Theme.View
|
||||
Kirigami.Theme.inherit: false
|
||||
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
|
||||
border {
|
||||
color: Qt.rgba(borderColor.r, borderColor.g, borderColor.b, 0.3)
|
||||
width: 1
|
||||
}
|
||||
|
||||
shadow {
|
||||
xOffset: 0
|
||||
yOffset: 4
|
||||
color: Qt.rgba(0, 0, 0, 0.3)
|
||||
size: 8
|
||||
}
|
||||
}
|
||||
|
||||
contentItem: QQC2.ScrollView {
|
||||
// HACK: Hide unnecessary horizontal scrollbar (https://bugreports.qt.io/browse/QTBUG-83890)
|
||||
QQC2.ScrollBar.horizontal.policy: QQC2.ScrollBar.AlwaysOff
|
||||
|
||||
ListView {
|
||||
id: userListView
|
||||
clip: true
|
||||
|
||||
model: UserFilterModel {
|
||||
id: userListFilterModel
|
||||
sourceModel: RoomManager.userListModel
|
||||
filterText: userListSearchField.text
|
||||
|
||||
onFilterTextChanged: {
|
||||
if (filterText.length > 0 && !userListSearchPopup.visible) {
|
||||
userListSearchPopup.open();
|
||||
} else if (filterText.length <= 0 && userListSearchPopup.visible) {
|
||||
userListSearchPopup.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate: Delegates.RoundedItemDelegate {
|
||||
id: userListItem
|
||||
|
||||
required property string userId
|
||||
required property url avatar
|
||||
required property string name
|
||||
required property int powerLevel
|
||||
required property string powerLevelString
|
||||
|
||||
text: name
|
||||
|
||||
contentItem: RowLayout {
|
||||
KirigamiComponents.Avatar {
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.medium
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.medium
|
||||
source: userListItem.avatar
|
||||
name: userListItem.name
|
||||
}
|
||||
|
||||
Delegates.SubtitleContentItem {
|
||||
itemDelegate: userListItem
|
||||
subtitle: userListItem.userId
|
||||
labelItem.textFormat: Text.PlainText
|
||||
subtitleItem.textFormat: Text.PlainText
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
QQC2.Label {
|
||||
visible: userListItem.powerLevel > 0
|
||||
|
||||
text: userListItem.powerLevelString
|
||||
color: Kirigami.Theme.disabledTextColor
|
||||
textFormat: Text.PlainText
|
||||
wrapMode: Text.NoWrap
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: {
|
||||
userListSearchPopup.close();
|
||||
(powerLevelDialog.createObject(root.QQC2.Overlay.overlay, {
|
||||
room: root.room,
|
||||
userId: userListItem.userId,
|
||||
powerLevel: userListItem.powerLevel
|
||||
}) as PowerLevelDialog).open();
|
||||
}
|
||||
|
||||
Component {
|
||||
id: powerLevelDialog
|
||||
PowerLevelDialog {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
visible: root.room.canSendState("m.room.power_levels")
|
||||
title: i18nc("@title", "Default permissions")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
visible: root.room.canSendState("m.room.power_levels")
|
||||
enabled: root.room.canSendState("m.room.power_levels")
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: root.permissionsModel
|
||||
@@ -269,11 +72,49 @@ FormCard.FormCardPage {
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
visible: root.room.canSendState("m.room.power_levels")
|
||||
title: i18nc("@title", "Basic permissions")
|
||||
title: i18nc("@title", "Messages")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
visible: root.room.canSendState("m.room.power_levels")
|
||||
enabled: root.room.canSendState("m.room.power_levels")
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: root.permissionsModel
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
return sourceModel.data(sourceModel.index(source_row, 0, source_parent), PermissionsModel.IsMessagePermissionRole);
|
||||
}
|
||||
}
|
||||
delegate: FormCard.FormComboBoxDelegate {
|
||||
required property string name
|
||||
required property string subtitle
|
||||
required property string type
|
||||
required property int level
|
||||
required property string levelName
|
||||
|
||||
text: name
|
||||
description: subtitle
|
||||
textRole: "name"
|
||||
valueRole: "value"
|
||||
model: root.powerLevelModel
|
||||
Component.onCompleted: {
|
||||
let index = indexOfValue(level)
|
||||
if (index === -1) {
|
||||
displayText = levelName;
|
||||
} else {
|
||||
currentIndex = index;
|
||||
}
|
||||
}
|
||||
onCurrentValueChanged: if (root.room.canSendState("m.room.power_levels")) {
|
||||
root.permissionsModel.setPowerLevel(type, currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title", "Moderation")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
enabled: root.room.canSendState("m.room.power_levels")
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: root.permissionsModel
|
||||
@@ -309,18 +150,15 @@ FormCard.FormCardPage {
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
visible: root.room.canSendState("m.room.power_levels")
|
||||
title: i18nc("@title", "Event permissions")
|
||||
title: i18nc("@title", "General")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
visible: root.room.canSendState("m.room.power_levels")
|
||||
enabled: root.room.canSendState("m.room.power_levels")
|
||||
Repeater {
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: root.permissionsModel
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
let isBasicPermissionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PermissionsModel.IsBasicPermissionRole);
|
||||
let isDefaultValueRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PermissionsModel.IsDefaultValueRole);
|
||||
return !isBasicPermissionRole && !isDefaultValueRole;
|
||||
return sourceModel.data(sourceModel.index(source_row, 0, source_parent), PermissionsModel.IsGeneralPermissionRole);
|
||||
}
|
||||
}
|
||||
delegate: FormCard.FormComboBoxDelegate {
|
||||
@@ -348,7 +186,59 @@ FormCard.FormCardPage {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FormCard.FormHeader {
|
||||
title: i18nc("@title", "Other Events")
|
||||
}
|
||||
FormCard.FormCard {
|
||||
enabled: root.room.canSendState("m.room.power_levels")
|
||||
|
||||
Repeater {
|
||||
id: otherEventsRepeater
|
||||
|
||||
model: KSortFilterProxyModel {
|
||||
sourceModel: root.permissionsModel
|
||||
filterRowCallback: function (source_row, source_parent) {
|
||||
let isBasicPermissionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PermissionsModel.IsBasicPermissionRole);
|
||||
let isDefaultValueRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PermissionsModel.IsDefaultValueRole);
|
||||
let isMessagePermissionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PermissionsModel.IsMessagePermissionRole);
|
||||
let isGeneralPermissionRole = sourceModel.data(sourceModel.index(source_row, 0, source_parent), PermissionsModel.IsGeneralPermissionRole);
|
||||
return !isBasicPermissionRole && !isDefaultValueRole && !isMessagePermissionRole && !isGeneralPermissionRole;
|
||||
}
|
||||
}
|
||||
delegate: FormCard.FormComboBoxDelegate {
|
||||
required property string name
|
||||
required property string subtitle
|
||||
required property string type
|
||||
required property int level
|
||||
required property string levelName
|
||||
|
||||
text: name
|
||||
description: subtitle
|
||||
textRole: "name"
|
||||
valueRole: "value"
|
||||
model: root.powerLevelModel
|
||||
Component.onCompleted: {
|
||||
let index = indexOfValue(level)
|
||||
if (index === -1) {
|
||||
displayText = levelName;
|
||||
} else {
|
||||
currentIndex = index;
|
||||
}
|
||||
}
|
||||
onCurrentValueChanged: if (root.room.canSendState("m.room.power_levels")) {
|
||||
root.permissionsModel.setPowerLevel(type, currentValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
FormCard.FormDelegateSeparator {
|
||||
below: addNewEventDelegate
|
||||
visible: otherEventsRepeater.count > 0
|
||||
}
|
||||
FormCard.AbstractFormDelegate {
|
||||
id: addNewEventDelegate
|
||||
|
||||
Layout.fillWidth: true
|
||||
|
||||
contentItem: RowLayout {
|
||||
|
||||
Reference in New Issue
Block a user