chore(CommunityPermissions): Permissions model & backend API simplified, dirty state handling simplified

Additionally, permission types metadata excluded to PermissionTypes
singleton component, PermissionsDropdown simplified by using inline
components.
This commit is contained in:
Michał Cieślak 2023-02-11 20:03:57 +01:00 committed by Michał
parent 7388bbfef3
commit 9757002c7d
10 changed files with 188 additions and 221 deletions

View File

@ -19,11 +19,13 @@ SplitView {
SplitView.fillWidth: true
SplitView.fillHeight: true
color: Theme.palette.statusAppLayout.rightPanelBackgroundColor
CommunityPermissionsView {
anchors {
fill: parent
margins: 50
}
store: CommunitiesStore {
id: mockedCommunity
permissionsModel: PermissionsModel.permissionsModel

View File

@ -13,24 +13,16 @@ QtObject {
Component.onCompleted:
append([
{
isPrivate: true,
holdingsListModel: root.createHoldingsModel1(),
permissionsObjectModel: {
key: 1,
text: "Become member",
imageSource: "in-contacts"
},
channelsListModel: root.createChannelsModel1()
channelsListModel: root.createChannelsModel1(),
permissionType: PermissionTypes.Type.Admin,
isPrivate: true
},
{
isPrivate: false,
holdingsListModel: root.createHoldingsModel2(),
permissionsObjectModel: {
key: 2,
text: "View and post",
imageSource: "edit"
},
channelsListModel: root.createChannelsModel2()
channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member,
isPrivate: false
}
])
}
@ -39,14 +31,10 @@ QtObject {
Component.onCompleted:
append([
{
isPrivate: true,
holdingsListModel: root.createHoldingsModel3(),
permissionsObjectModel: {
key: 1,
text: "Become member",
imageSource: "in-contacts"
},
channelsListModel: root.createChannelsModel1()
channelsListModel: root.createChannelsModel1(),
permissionType: PermissionTypes.Type.Admin,
isPrivate: true,
}
])
}
@ -55,44 +43,28 @@ QtObject {
Component.onCompleted:
append([
{
isPrivate: true,
holdingsListModel: root.createHoldingsModel4(),
permissionsObjectModel: {
key: 1,
text: "Become member",
imageSource: "in-contacts"
},
channelsListModel: root.createChannelsModel1()
channelsListModel: root.createChannelsModel1(),
permissionType: PermissionTypes.Type.Admin,
isPrivate: true
},
{
isPrivate: false,
holdingsListModel: root.createHoldingsModel3(),
permissionsObjectModel: {
key: 2,
text: "View and post",
imageSource: "edit"
},
channelsListModel: root.createChannelsModel2()
channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member,
isPrivate: false
},
{
isPrivate: false,
holdingsListModel: root.createHoldingsModel2(),
permissionsObjectModel: {
key: 2,
text: "View and post",
imageSource: "edit"
},
channelsListModel: root.createChannelsModel2()
channelsListModel: root.createChannelsModel2(),
permissionType: PermissionTypes.Type.Member,
isPrivate: false
},
{
isPrivate: false,
channelsListModel: root.createChannelsModel2(),
holdingsListModel: root.createHoldingsModel1(),
permissionsObjectModel: {
key: 2,
text: "View and post",
imageSource: "edit"
},
channelsListModel: root.createChannelsModel2()
permissionType: PermissionTypes.Type.Member,
isPrivate: false
}
])
}

View File

@ -12,9 +12,9 @@ Control{
id: root
property var holdingsListModel
property string permissionName
property var permissionImageSource
property var channelsListModel
property int permissionType: PermissionTypes.Type.None
property bool isPrivate: false
signal editClicked
@ -121,8 +121,8 @@ Control{
StatusListItemTag {
height: d.flowRowHeight
title: root.permissionName
asset.name: root.permissionImageSource
title: PermissionTypes.getName(root.permissionType)
asset.name: PermissionTypes.getIcon(root.permissionType)
asset.isImage: false
asset.bgColor: "transparent"
closeButtonVisible: false

View File

@ -1,7 +1,67 @@
pragma Singleton
import QtQml 2.14
import StatusQ.Core.Theme 0.1
QtObject {
enum Type {
None, Admin, Member, Moderator, ViewAndPost, Read
}
function getName(type) {
switch (type) {
case PermissionTypes.Type.Admin:
return qsTr("Become admin")
case PermissionTypes.Type.Member:
return qsTr("Become member")
case PermissionTypes.Type.Moderator:
return qsTr("Moderate")
case PermissionTypes.Type.ViewAndPost:
return qsTr("View and post")
case PermissionTypes.Type.Read:
return qsTr("View only")
}
return ""
}
function getIcon(type) {
switch (type) {
case PermissionTypes.Type.Admin:
return "admin"
case PermissionTypes.Type.Member:
return "in-contacts"
case PermissionTypes.Type.Moderator:
return "arbitrator"
case PermissionTypes.Type.ViewAndPost:
return "edit"
case PermissionTypes.Type.Read:
return "show"
}
return ""
}
function getDescription(type) {
switch (type) {
case PermissionTypes.Type.Admin:
const generalInfo = qsTr("Members who meet the requirements will be allowed to create and edit permissions, token sales, airdrops and subscriptions")
const warning = qsTr("Be careful with assigning this permission.")
const warningExplanation = qsTr("Only the community owner can modify admin permissions")
const warningStyled = `<font color="${Theme.palette.dangerColor1}">${warning}</font>`
return `${generalInfo}<br><br>${warningStyled} ${warningExplanation}`
case PermissionTypes.Type.Member:
return qsTr("Anyone who meets the requirements will be allowed to join your community")
case PermissionTypes.Type.Moderator:
return qsTr("Members who meet the requirements will be allowed to read, write, ban members and pin messages in the selected channels")
case PermissionTypes.Type.ViewAndPost:
return qsTr("Members who meet the requirements will be allowed to read and write in the selected channels")
case PermissionTypes.Type.Read:
return qsTr("Members who meet the requirements will be allowed to read the selected channels")
}
return ""
}
}

View File

@ -21,7 +21,7 @@ StatusDropdown {
Add, Update
}
signal done(int permissionType, string title, string asset)
signal done(int permissionType)
width: d.width
padding: d.padding
@ -54,6 +54,35 @@ StatusDropdown {
readonly property int descriptionLineHeight: 18
readonly property int buttonTopMargin: 32
component CustomSeparator: Item {
property alias text: baseText.text
StatusBaseText {
id: baseText
anchors.margins: d.extraMarginForText
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
color: Theme.palette.baseColor1
font.pixelSize: d.sectionFontSize
elide: Text.ElideRight
}
}
component CustomPermissionListItem: PermissionListItem {
required property int permissionType
required property string description
title: PermissionTypes.getName(permissionType)
description: PermissionTypes.getDescription(permissionType)
asset.name: PermissionTypes.getIcon(permissionType)
checked: d.initialPermissionType === permissionType
buttonGroup: group
}
}
ButtonGroup {
@ -63,107 +92,47 @@ StatusDropdown {
contentItem: ColumnLayout {
spacing: 0
Item {
CustomSeparator {
Layout.fillWidth: true
Layout.preferredHeight: d.sectionHeight
StatusBaseText {
anchors.margins: d.extraMarginForText
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("Community")
color: Theme.palette.baseColor1
font.pixelSize: d.sectionFontSize
elide: Text.ElideRight
}
text: qsTr("Community")
}
PermissionListItem {
readonly property int permissionType: PermissionTypes.Type.Admin
readonly property string description: {
const generalInfo = qsTr("Members who meet the requirements will be allowed to create and edit permissions, token sales, airdrops and subscriptions")
const warning = qsTr("Be careful with assigning this permission.")
const warningExplanation = qsTr("Only the community owner can modify admin permissions")
const warningStyled = `<font color="${Theme.palette.dangerColor1}">${warning}</font>`
return `${generalInfo}<br><br>${warningStyled} ${warningExplanation}`
}
title: qsTr("Become admin")
asset.name: "admin"
checked: d.initialPermissionType === permissionType
buttonGroup: group
CustomPermissionListItem {
permissionType: PermissionTypes.Type.Admin
enabled: root.enableAdminPermission
Layout.fillWidth: true
}
PermissionListItem {
readonly property int permissionType: PermissionTypes.Type.Member
readonly property string description:
qsTr("Anyone who meets the requirements will be allowed to join your community")
title: qsTr("Become member")
asset.name: "in-contacts"
checked: d.initialPermissionType === permissionType
buttonGroup: group
CustomPermissionListItem {
permissionType: PermissionTypes.Type.Member
Layout.fillWidth: true
}
Item {
CustomSeparator {
Layout.fillWidth: true
Layout.preferredHeight: d.sectionHeight
StatusBaseText {
anchors.margins: d.extraMarginForText
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
text: qsTr("Channels")
color: Theme.palette.baseColor1
font.pixelSize: d.sectionFontSize
elide: Text.ElideRight
}
text: qsTr("Channels")
}
PermissionListItem {
readonly property int permissionType: PermissionTypes.Type.Moderator
readonly property string description:
qsTr("Members who meet the requirements will be allowed to read, write, ban members and pin messages in the selected channels")
title: qsTr("Moderate")
asset.name: "arbitrator"
checked: d.initialPermissionType === permissionType
buttonGroup: group
CustomPermissionListItem {
permissionType: PermissionTypes.Type.Moderator
Layout.fillWidth: true
}
PermissionListItem {
readonly property int permissionType: PermissionTypes.Type.ViewAndPost
readonly property string description:
qsTr("Members who meet the requirements will be allowed to read and write in the selected channels")
title: qsTr("View and post")
asset.name: "edit"
checked: d.initialPermissionType === permissionType
buttonGroup: group
CustomPermissionListItem {
permissionType: PermissionTypes.Type.ViewAndPost
Layout.fillWidth: true
}
PermissionListItem {
readonly property int permissionType: PermissionTypes.Type.Read
readonly property string description:
qsTr("Members who meet the requirements will be allowed to read the selected channels")
title: qsTr("View only")
asset.name: "show"
checked: d.initialPermissionType === permissionType
buttonGroup: group
CustomPermissionListItem {
permissionType: PermissionTypes.Type.Read
Layout.fillWidth: true
}
@ -198,9 +167,7 @@ StatusDropdown {
text: root.mode === PermissionsDropdown.Mode.Add ? qsTr("Add") : qsTr("Update")
enabled: !!group.checkedButton
onClicked: root.done(group.checkedButton.item.permissionType,
group.checkedButton.item.title,
group.checkedButton.item.asset.name)
onClicked: root.done(group.checkedButton.item.permissionType)
}
}
}

View File

@ -4,4 +4,5 @@ HoldingTypes 1.0 HoldingTypes.qml
HoldingsDropdown 1.0 HoldingsDropdown.qml
InDropdown 1.0 InDropdown.qml
PermissionItem 1.0 PermissionItem.qml
singleton PermissionTypes 1.0 PermissionTypes.qml
singleton TokenCategories 1.0 TokenCategories.qml

View File

@ -1,8 +1,9 @@
import QtQuick 2.14
import AppLayouts.Chat.controls.community 1.0
import AppLayouts.Chat.layouts 1.0
import AppLayouts.Chat.views.communities 1.0
import AppLayouts.Chat.stores 1.0
import AppLayouts.Chat.views.communities 1.0
import utils 1.0
@ -41,7 +42,7 @@ SettingsPageLayout {
property int permissionIndexToEdit
property ListModel holdingsToEditModel: ListModel {}
property var permissionsToEditObject
property int permissionTypeToEdit: PermissionTypes.Type.None
property ListModel channelsToEditModel: ListModel {}
property bool isPrivateToEditValue: false
@ -57,8 +58,8 @@ SettingsPageLayout {
function initializeData() {
holdingsToEditModel = defaultListObject.createObject(d)
permissionsToEditObject = null
channelsToEditModel = defaultListObject.createObject(d)
permissionTypeToEdit = PermissionTypes.Type.None
isPrivateToEditValue = false
}
}
@ -151,14 +152,16 @@ SettingsPageLayout {
store: root.store
isEditState: root.state === d.editPermissionViewState
holdingsModel: d.holdingsToEditModel
permissionObject: d.permissionsToEditObject
channelsModel: d.channelsToEditModel
permissionType: d.permissionTypeToEdit
isPrivate: d.isPrivateToEditValue
onCreatePermissionClicked: {
root.store.createPermission(dirtyValues.holdingsModel,
dirtyValues.permissionObject,
dirtyValues.permissionType,
dirtyValues.isPrivate,
dirtyValues.channelsModel)
@ -172,7 +175,7 @@ SettingsPageLayout {
root.store.editPermission(
d.permissionIndexToEdit,
dirtyValues.holdingsModel,
dirtyValues.permissionObject,
dirtyValues.permissionType,
dirtyValues.channelsModel,
dirtyValues.isPrivate)
}
@ -203,8 +206,8 @@ SettingsPageLayout {
d.permissionIndexToEdit = index
d.holdingsToEditModel = item.holdingsListModel
d.permissionsToEditObject = item.permissionsObjectModel
d.channelsToEditModel = item.channelsListModel
d.permissionTypeToEdit = item.permissionType
d.isPrivateToEditValue = item.isPrivate
root.state = d.editPermissionViewState
}

View File

@ -131,17 +131,13 @@ QtObject {
ListElement { key: "general"; iconSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "#general"}
}
function createPermission(holdings, permissions, isPrivate, channels, index = null) {
function createPermission(holdings, permissionType, isPrivate, channels, index = null) {
// TO BE REPLACED: It shold just be a call to the backend sharing `holdings`, `permissions`, `channels` and `isPrivate` properties.
const permission = {
isPrivate: true,
holdingsListModel: [],
permissionsObjectModel: {
key: "",
text: "",
imageSource: ""
},
channelsListModel: [],
permissionType,
isPrivate
}
// Setting HOLDINGS:
@ -157,11 +153,6 @@ QtObject {
})
}
// Setting PERMISSIONS:
permission.permissionsObjectModel.key = permissions.key
permission.permissionsObjectModel.text = permissions.text
permission.permissionsObjectModel.imageSource = permissions.imageSource
// Setting CHANNELS:
for (let c = 0; c < channels.count; c++) {
const entry = channels.get(c)
@ -174,10 +165,6 @@ QtObject {
})
}
// Setting PRIVATE permission property:
permission.isPrivate = isPrivate
if (index !== null) {
// Edit permission model:
console.log("TODO: Edit permissions - backend call")
@ -189,16 +176,16 @@ QtObject {
}
}
function editPermission(index, holdings, permissions, channels, isPrivate) {
function editPermission(index, holdings, permissionType, channels, isPrivate) {
// TO BE REPLACED: Call to backend
createPermission(holdings, permissions, isPrivate, channels, index)
createPermission(holdings, permissionType, isPrivate, channels, index)
}
function duplicatePermission(index) {
// TO BE REPLACED: Call to backend
console.log("TODO: Duplicate permissions - backend call")
const permission = root.permissionsModel.get(index)
createPermission(permission.holdingsListModel, permission.permissionsObjectModel,
createPermission(permission.holdingsListModel, permission.permissionType,
permission.isPrivate, permission.channelsListModel)
}

View File

@ -27,43 +27,21 @@ StatusScrollView {
property int viewWidth: 560 // by design
property bool isEditState: false
readonly property bool dirty: {
// Holdings:
if (!holdingsModelComparator.equal)
return true
readonly property bool dirty:
!holdingsModelComparator.equal ||
!channelsModelComparator.equal ||
root.isPrivate !== d.dirtyValues.isPrivate ||
root.permissionType !== d.dirtyValues.permissionType
// Channels
if (!channelsModelComparator.equal)
return true
// Is private
if (root.isPrivate !== d.dirtyValues.isPrivate)
return true
// Permissions:
let dirtyPermissionObj = false
if(root.permissionObject && d.dirtyValues.permissionObject.key !== null) {
dirtyPermissionObj = (d.dirtyValues.permissionObject.key !== root.permissionObject.key) ||
(d.dirtyValues.permissionObject.text !== root.permissionObject.text) ||
(d.dirtyValues.permissionObject.imageSource !== root.permissionObject.imageSource)
} else {
dirtyPermissionObj = d.dirtyValues.permissionObject.key !== null
}
return dirtyPermissionObj
}
property int permissionType: PermissionTypes.Type.None
property bool isPrivate: false
// roles: type, key, name, amount, imageSource
property var holdingsModel: ListModel {}
// roles: key, text, imageSource
property var permissionObject
// roles: itemId, text, emoji, color
property var channelsModel: ListModel {}
property bool isPrivate: false
readonly property alias dirtyValues: d.dirtyValues
signal createPermissionClicked
@ -100,17 +78,9 @@ StatusScrollView {
readonly property int dropdownHorizontalOffset: 4
readonly property int dropdownVerticalOffset: 1
property int permissionType: PermissionTypes.Type.None
readonly property bool isCommunityPermission:
permissionType === PermissionTypes.Type.Admin ||
permissionType === PermissionTypes.Type.Member
onPermissionTypeChanged: {
d.dirtyValues.isPrivate =
(permissionType === PermissionTypes.Type.Admin) ||
(permissionType === PermissionTypes.Type.Moderator)
}
dirtyValues.permissionType === PermissionTypes.Type.Admin ||
dirtyValues.permissionType === PermissionTypes.Type.Member
onIsCommunityPermissionChanged: {
if (isCommunityPermission) {
@ -128,14 +98,14 @@ StatusScrollView {
readonly property ListModel holdingsModel: ListModel {}
readonly property ListModel channelsModel: ListModel {}
readonly property QtObject permissionObject: QtObject {
property var key: null
property string text: ""
property string imageSource: ""
}
property int permissionType: PermissionTypes.Type.None
property bool isPrivate: false
Binding on isPrivate {
value: (d.dirtyValues.permissionType === PermissionTypes.Type.Admin) ||
(d.dirtyValues.permissionType === PermissionTypes.Type.Moderator)
}
function getHoldingIndex(key) {
return ModelUtils.indexOf(holdingsModel, "key", key)
}
@ -162,11 +132,7 @@ StatusScrollView {
["type", "key", "name", "amount", "imageSource", "shortName"]))
// Permissions:
d.dirtyValues.permissionObject.key = root.permissionObject ? root.permissionObject.key : null
d.dirtyValues.permissionObject.text = root.permissionObject ? root.permissionObject.text : ""
d.dirtyValues.permissionObject.imageSource = root.permissionObject ? root.permissionObject.imageSource : ""
d.permissionType = root.permissionObject ? root.permissionObject.key : PermissionTypes.Type.None
d.dirtyValues.permissionType = root.permissionType
// Channels
d.dirtyValues.channelsModel.clear()
@ -175,7 +141,7 @@ StatusScrollView {
ModelUtils.modelToArray(root.channelsModel,
["itemId", "text", "emoji", "color", "operator"]))
if (root.channelsModel && (root.channelsModel.count || d.dirtyValues.permissionObject.key === null)) {
if (root.channelsModel && (root.channelsModel.count || d.dirtyValues.permissionType === PermissionTypes.Type.None)) {
inSelector.wholeCommunitySelected = false
inSelector.itemsModel = d.dirtyValues.channelsModel
} else {
@ -195,7 +161,7 @@ StatusScrollView {
contentWidth: mainLayout.width
contentHeight: mainLayout.height
onPermissionObjectChanged: d.loadInitValues()
onPermissionTypeChanged: d.loadInitValues()
ColumnLayout {
id: mainLayout
@ -388,26 +354,33 @@ StatusScrollView {
useIcons: true
title: qsTr("Is allowed to")
defaultItemText: qsTr("Example: View and post")
itemsModel: d.dirtyValues.permissionObject.key ? d.dirtyValues.permissionObject : null
addButton.visible: !root.permissionObject
QtObject {
id: permissionItemModelData
readonly property int key: d.dirtyValues.permissionType
readonly property string text: PermissionTypes.getName(key)
readonly property string imageSource: PermissionTypes.getIcon(key)
}
itemsModel: d.dirtyValues.permissionType !== PermissionTypes.Type.None
? permissionItemModelData : null
addButton.visible: d.dirtyValues.permissionType === PermissionTypes.Type.None
PermissionsDropdown {
id: permissionsDropdown
initialPermissionType: d.permissionType
initialPermissionType: d.dirtyValues.permissionType
enableAdminPermission: root.store.isOwner
onDone: {
if (d.permissionType === permissionType) {
if (d.dirtyValues.permissionType === permissionType) {
permissionsDropdown.close()
return
}
d.permissionType = permissionType
d.dirtyValues.permissionObject.key = permissionType
d.dirtyValues.permissionObject.text = title
d.dirtyValues.permissionObject.imageSource = asset
d.dirtyValues.permissionType = permissionType
permissionsDropdown.close()
}
}
@ -555,7 +528,7 @@ StatusScrollView {
Layout.leftMargin: 16
Layout.rightMargin: Layout.leftMargin
enabled: d.permissionType !== PermissionTypes.Type.Admin
enabled: d.dirtyValues.permissionType !== PermissionTypes.Type.Admin
checked: d.dirtyValues.isPrivate
onToggled: d.dirtyValues.isPrivate = checked
}
@ -576,7 +549,7 @@ StatusScrollView {
Layout.topMargin: conflictPanel.visible ? conflictPanel.Layout.topMargin : 24 // by design
text: qsTr("Create permission")
enabled: d.dirtyValues.holdingsModel.count > 0
&& d.dirtyValues.permissionObject.key !== null
&& d.dirtyValues.permissionType !== PermissionTypes.Type.None
&& (d.dirtyValues.channelsModel.count > 0 || d.isCommunityPermission)
Layout.preferredHeight: 44
Layout.alignment: Qt.AlignHCenter

View File

@ -55,8 +55,10 @@ StatusScrollView {
Repeater {
model: root.store.permissionsModel
delegate: PermissionItem {
Layout.preferredWidth: root.viewWidth
holdingsListModel: SortFilterProxyModel {
sourceModel: model.holdingsListModel
@ -67,8 +69,8 @@ StatusScrollView {
expression: d.holdingsTextFormat(model.type, model.name, model.amount)
}
}
permissionName: model.permissionsObjectModel.text
permissionImageSource: model.permissionsObjectModel.imageSource
permissionType: model.permissionType
SortFilterProxyModel {
id: proxiedChannelsModel