chore: Adding new component ChannelPermissionsModelEditor

The purpose of this component is to hold the dirty permissions edited my the user.

This is a functional component used to create/edit/delete permissions. The edited permissions are detached from the backend.
This commit is contained in:
Alex Jbanca 2024-02-06 11:14:22 +02:00 committed by Alex Jbanca
parent fc81568fe9
commit 35c1475403
2 changed files with 344 additions and 0 deletions

View File

@ -0,0 +1,343 @@
import QtQuick 2.15
import StatusQ 0.1
import AppLayouts.Communities.controls 1.0
import SortFilterProxyModel 0.2
import StatusQ.Core.Utils 0.1 as StatusQUtils
import utils 1.0
/// Helper component for editing channel permissions
/// This component will create the necessary temporary models to be used until the changes are saved
QtObject {
id: root
// Input properties
// Model containing all channels
required property var channelsModel
// Model containing all permissions for the current community
required property var permissionsModel
// Channel ID
required property string channelId
// Channel name
required property string name
// Channel color
required property string color
// Channel emoji
required property string emoji
// Specify whether the channel is being edited or created
required property bool newChannelMode
// Output properties
// The edited channel permissions model
readonly property alias channelPermissionsModel: d.channelPermissionsModel
// Live model of channels contaning the temporarely edited channel
readonly property alias liveChannelsModel: d.liveChannelsModel
readonly property alias dirtyPermissions: d.channelPermissionsModel.dirty
// Input functions
// Function creating a new permission based on the given arguments
function appendPermission(holdings, channels, permissionType, isPrivate) {
d.appendPermission(holdings, channels, permissionType, isPrivate)
}
// Function editing a permission based on the given arguments
function editPermission(key, permissionType, holdings, channels, isPrivate) {
d.editPermission(key, permissionType, holdings, channels, isPrivate)
}
// Function duplicating a permission. The new permission will be have a different id and key
function duplicatePermission(index) {
const permission = channelPermissionsModel.get(index)
if (!permission)
return
permission.id = Utils.uuid()
permission.key = Utils.uuid()
permission.holdingsListModel = d.newHoldingsModel(StatusQUtils.ModelUtils.modelToArray(permission.holdingsListModel))
permission.channelsListModel = d.newChannelsModel(StatusQUtils.ModelUtils.modelToArray(permission.channelsListModel))
channelPermissionsModel.append(permission)
}
// Function removing a permission by index
function removePermission(index) {
channelPermissionsModel.remove(index, 1);
}
// Output functions
// Function returning the list of added permissions
// The returned list contains the permissions that were added since the last reset
// Each item contains a map of role names and data value pairs as defined in the model
function getAddedPermissions() {
return d.flattenPermissions(channelPermissionsModel.getInsertedItems())
}
// Function returning the list of removed permissions
// The returned list contains the permissions that were removed since the last reset
// This list contains the permissions that are no longer available in the root.channelPermissionsModel, but are still available in the root.permissionsModel
// Each item contains a map of role names and data value pairs as defined in the model
function getRemovedPermissions() {
return d.flattenPermissions(channelPermissionsModel.getRemovedItems())
}
// Function returning the list of edited permissions
// The returned list contains the permissions that were edited since the last reset
// Each item contains a map of role names and data value pairs as defined in the model
function getEditedPermissions() {
return d.flattenPermissions(channelPermissionsModel.getEditedItems())
}
// This function resets the temporary models
// The dirtyPermissions property will be set to false
function reset() {
d.reset()
}
//internals
readonly property QtObject d: QtObject {
id: d
signal resetDone()
function reset() {
channelPermissionsModel.revert();
liveChannelsModel.revert();
d.resetDone();
}
function appendPermission(holdings, channels, permissionType, isPrivate) {
var permissionsModelData = {
id: Utils.uuid(),
key: Utils.uuid(),
holdingsListModel: d.newHoldingsModel(holdings),
channelsListModel: d.newChannelsModel(channels),
"permissionType": permissionType,
"isPrivate": isPrivate
};
d.channelPermissionsModel.append(permissionsModelData)
}
function editPermission(key, permissionType, holdings, channels, isPrivate) {
const index = StatusQUtils.ModelUtils.indexOf(d.channelPermissionsModel, "key", key)
if (index === -1)
return
const permissionItem = d.channelPermissionsModel.get(index)
d.channelPermissionsModel.set(index, {
"permissionType": permissionType,
"channelsListModel": d.newEditedChannelsModel(channels, permissionItem.channelsListModel),
"holdingsListModel": d.newEditedHoldingsModel(holdings, permissionItem.holdingsListModel),
"isPrivate": isPrivate
})
}
// Creates a new channel model to be used in the permissions model
// Expected roles: "key"
function newChannelsModel(channelsArray) {
var channelsModel = selfDestroyingModel.createObject(d.channelPermissionsModel);
for (var i = 0; i < channelsArray.length; i++) {
channelsModel.append({ key: channelsArray[i].key })
}
return channelsModel;
}
// Creates a new writable channel model on top of base model to be used in the permissions model
function newEditedChannelsModel(channelsArray, baseModel) {
var channelsModel = selfDestroyingWritableModel.createObject(d.channelPermissionsModel, {sourceModel: baseModel});
const count = channelsModel.rowCount()
for (var i = 0; i < count; i++) {
const ok = channelsModel.remove(0)
console.assert(ok
, "Failed to remove channel")
}
for (var i = 0; i < channelsArray.length; i++) {
const ok = channelsModel.append({
key: channelsArray[i].key,
})
console.assert(ok, "Failed to append channel");
}
return channelsModel;
}
// Creates a new holdings model to be used in the permissions model
function newHoldingsModel(holdingsArray, sourceModel) {
var holdingsModel = selfDestroyingModel.createObject(d.channelPermissionsModel);
for (var i = 0; i < holdingsArray.length; i++) {
holdingsModel.append(holdingsArray[i]);
}
return holdingsModel;
}
function newEditedHoldingsModel(holdingsArray, baseModel) {
var holdingsModel = selfDestroyingWritableModel.createObject(d.channelPermissionsModel, {sourceModel: baseModel});
const count = holdingsModel.rowCount()
for (var i = 0; i < count; i++) {
const ok = holdingsModel.remove(0)
console.assert(ok, "Failed to remove holding")
}
for (var i = 0; i < holdingsArray.length; i++) {
const ok = holdingsModel.append(holdingsArray[i])
console.assert(ok, "Failed to append holding");
}
return holdingsModel;
}
function flattenPermissions(permissionsItems) {
for (var i = 0; i < permissionsItems.length; i++) {
permissionsItems[i].holdingsListModel = StatusQUtils.ModelUtils.modelToArray(permissionsItems[i].holdingsListModel)
permissionsItems[i].channelsListModel = StatusQUtils.ModelUtils.modelToArray(permissionsItems[i].channelsListModel)
}
return permissionsItems
}
property Connections chatIdConnections: Connections {
target: root
enabled: root.newChannelMode
property string previousChannelId: ""
Component.onCompleted: previousChannelId = root.channelId
/// go through all the channels and replace the old channel id with the new one
function onChannelIdChanged() {
if (previousChannelId === root.channelId)
return;
if (previousChannelId == "") {
previousChannelId = root.channelId;
return;
}
for (var i = 0; i < liveChannelsModel.rowCount(); i++) {
if (liveChannelsModel.get(i).itemId === previousChannelId) {
liveChannelsModel.set(i, { itemId: root.channelId })
break;
}
}
for (var i = 0; i < channelPermissionsModel.rowCount(); i++) {
const currentItem = channelPermissionsModel.get(i)
const channelsListModel = currentItem.channelsListModel
for (var j = 0; j < channelsListModel.rowCount(); j++) {
if (channelsListModel.get(j).key === previousChannelId) {
channelsListModel.set(j, { key: root.channelId })
break;
}
}
}
previousChannelId = root.channelId;
}
}
// Channel permissions model containing the temporarely edited permissions
property WritableProxyModel channelPermissionsModel: WritableProxyModel {
sourceModel: SortFilterProxyModel {
id: filteredPermissionsModel
sourceModel: root.permissionsModel
filters: [
FastExpressionFilter {
function filterPredicate(id, permissionType) {
return !PermissionTypes.isCommunityPermission(permissionType) && root.permissionsModel.belongsToChat(id, root.channelId)
}
expression: {
return filterPredicate(model.id, model.permissionType)
}
expectedRoles: [ "id", "permissionType" ]
}
]
}
}
// Channels model containing the temporarely edited channel
property WritableProxyModel liveChannelsModel: WritableProxyModel {
id: newChannelModel
function updateCurrentChannelProperty(nameAndValue) {
const index = StatusQUtils.ModelUtils.indexOf(newChannelModel, "itemId", root.channelId)
if (index !== -1) {
newChannelModel.set(index, nameAndValue)
}
}
Component.onCompleted: {
if (!root.newChannelMode)
return;
console.assert(newChannelModel.append({
itemId: root.channelId,
name: root.name,
emoji: root.emoji,
color: root.color
}), "Failed to add channel to channelsModel")
}
property Connections channelConnections: Connections {
target: root
function onColorChanged() {
newChannelModel.updateCurrentChannelProperty({"color": root.color})
}
function onNameChanged() {
newChannelModel.updateCurrentChannelProperty({"name": root.name})
}
function onEmojiChanged() {
newChannelModel.updateCurrentChannelProperty({"emoji": root.emoji})
}
}
sourceModel: root.channelsModel
}
// Used for dynamic model creation using Component.createObject
property Component selfDestroyingModel: Component {
ListModel {
id: model
Component.onCompleted: d.resetDone.connect(model.destroy)
Component.onDestruction: d.resetDone.disconnect(model.destroy)
}
}
property Component selfDestroyingWritableModel: Component {
WritableProxyModel {
id: model
Component.onCompleted: d.resetDone.connect(model.destroy)
Component.onDestruction: d.resetDone.disconnect(model.destroy)
}
}
}
}

View File

@ -0,0 +1 @@
ChannelPermissionsModelEditor 1.0 ChannelPermissionsModelEditor.qml