// SPDX-FileCopyrightText: 2022 James Graham // SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL import QtQuick 2.15 import QtQuick.Controls 2.15 as QQC2 import QtQuick.Layouts 1.15 import org.kde.kirigami 2.15 as Kirigami import org.kde.kirigamiaddons.labs.mobileform 0.1 as MobileForm import org.kde.kirigamiaddons.delegates 1.0 as Delegates import org.kde.kirigamiaddons.labs.components 1.0 as KirigamiComponents import org.kde.kitemmodels 1.0 import org.kde.neochat 1.0 Kirigami.ScrollablePage { id: root property NeoChatRoom room title: i18nc('@title:window', 'Permissions') topPadding: 0 leftPadding: 0 rightPadding: 0 UserListModel { id: userListModel room: root.room } ListModel { id: powerLevelModel } ColumnLayout { spacing: 0 MobileForm.FormHeader { Layout.fillWidth: true title: i18n("Privileged Users") } MobileForm.FormCard { Layout.fillWidth: true contentItem: ColumnLayout { spacing: 0 Repeater { model: KSortFilterProxyModel { sourceModel: userListModel sortRole: "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: MobileForm.FormTextDelegate { text: name description: userId contentItem.children: RowLayout { spacing: Kirigami.Units.largeSpacing QQC2.Label { visible: !room.canSendState("m.room.power_levels") text: powerLevelString color: Kirigami.Theme.disabledTextColor } QQC2.ComboBox { focusPolicy: Qt.NoFocus // provided by parent model: powerLevelModel textRole: "text" valueRole: "powerLevel" visible: room.canSendState("m.room.power_levels") Component.onCompleted: { /** * This is very silly but the only way to populate the model with * translated strings. Done here because the model needs to be filled * before the first delegate sets it's current index. */ if (powerLevelModel.count == 0) { powerLevelModel.append({"text": i18n("Member (0)"), "powerLevel": 0}); powerLevelModel.append({"text": i18n("Moderator (50)"), "powerLevel": 50}); powerLevelModel.append({"text": i18n("Admin (100)"), "powerLevel": 100}); } currentIndex = indexOfValue(powerLevel) } onActivated: { room.setUserPowerLevel(userId, currentValue) } } } } } MobileForm.FormDelegateSeparator { below: userListSearchCard } MobileForm.AbstractFormDelegate { id: userListSearchCard Layout.fillWidth: true visible: 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: { let currentUser = userListView.itemAtIndex(userListView.currentIndex); currentUser.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: 4 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: 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 string 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 ? ("image://" + 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 } } action: Kirigami.Action { id: editPowerLevelAction onTriggered: { userListSearchPopup.close() let dialog = powerLevelDialog.createObject(applicationWindow().overlay, { room: root.room, userId: userListItem.userId, powerLevel: userListItem.powerLevel }); dialog.open(); } } Component { id: powerLevelDialog PowerLevelDialog { id: powerLevelDialog } } } } } } } } } MobileForm.FormHeader { Layout.fillWidth: true visible: room.canSendState("m.room.power_levels") title: i18n("Default permissions") } MobileForm.FormCard { Layout.fillWidth: true visible: room.canSendState("m.room.power_levels") contentItem: ColumnLayout { spacing: 0 MobileForm.FormComboBoxDelegate { text: i18n("Default user power level") description: i18n("This is power level for all new users when joining the room") textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.defaultUserPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.defaultUserPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Default power level to set the room state") description: i18n("This is used for all state events that do not have their own entry here") textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.statePowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.statePowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Default power level to send messages") description: i18n("This is used for all message events that do not have their own entry here") textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.defaultEventPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.defaultEventPowerLevel = currentValue } } } MobileForm.FormHeader { Layout.fillWidth: true visible: room.canSendState("m.room.power_levels") title: i18n("Basic permissions") } MobileForm.FormCard { Layout.fillWidth: true visible: room.canSendState("m.room.power_levels") contentItem: ColumnLayout { spacing: 0 MobileForm.FormComboBoxDelegate { text: i18n("Invite users") textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.invitePowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.invitePowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Kick users") textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.kickPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.kickPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Ban users") textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.banPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.banPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Remove message sent by other users") textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.redactPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.redactPowerLevel = currentValue } } } MobileForm.FormHeader { Layout.fillWidth: true visible: room.canSendState("m.room.power_levels") title: i18n("Event permissions") } MobileForm.FormCard { Layout.fillWidth: true visible: room.canSendState("m.room.power_levels") contentItem: ColumnLayout { spacing: 0 MobileForm.FormComboBoxDelegate { text: i18n("Change user permissions") description: "m.room.power_levels" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.powerLevelPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.powerLevelPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Change the room name") description: "m.room.name" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.namePowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.namePowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Change the room avatar") description: "m.room.avatar" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.avatarPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.avatarPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Change the room canonical alias") description: "m.room.canonical_alias" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.canonicalAliasPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.canonicalAliasPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Change the room topic") description: "m.room.topic" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.topicPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.topicPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Enable encryption for the room") description: "m.room.encryption" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.encryptionPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.encryptionPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Change the room history visibility") description: "m.room.history_visibility" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.historyVisibilityPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.historyVisibilityPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Set pinned events") description: "m.room.pinned_events" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.pinnedEventsPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.pinnedEventsPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Upgrade the room") description: "m.room.tombstone" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.tombstonePowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.tombstonePowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Set the room server access control list (ACL)") description: "m.room.server_acl" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.serverAclPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.serverAclPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { visible: room.isSpace text: i18n("Set the children of this space") description: "m.space.child" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.spaceChildPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.spaceChildPowerLevel = currentValue } MobileForm.FormComboBoxDelegate { text: i18n("Set the parent space of this room") description: "m.space.parent" textRole: "text" valueRole: "powerLevel" model: powerLevelModel Component.onCompleted: currentIndex = indexOfValue(room.spaceChildPowerLevel) onCurrentValueChanged: if(room.canSendState("m.room.power_levels")) room.spaceParentPowerLevel = currentValue } } } } }