chore(CommunityPermissions): Permissions model and backend API simplified, separation of concerns improved

This commit is contained in:
Michał Cieślak 2023-02-13 11:40:13 +01:00 committed by Michał
parent 9757002c7d
commit c78eaef2b6
15 changed files with 303 additions and 146 deletions

View File

@ -0,0 +1,107 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import Storybook 1.0
import Models 1.0
import AppLayouts.Chat.controls.community 1.0
ColumnLayout {
id: root
property string panelText
property int type
property string key
property string amountText
property var assetKeys: []
property var collectibleKeys: []
QtObject {
id: d
readonly property bool ensLayout: root.type === HoldingTypes.Type.Ens
readonly property var holdingTypesModel: [
{ value: HoldingTypes.Type.Asset, text: "Asset" },
{ value: HoldingTypes.Type.Collectible, text: "Collectible" },
{ value: HoldingTypes.Type.Ens, text: "ENS" }
]
}
Label {
Layout.fillWidth: true
text: root.panelText
font.weight: Font.Bold
}
ColumnLayout {
Label {
Layout.fillWidth: true
text: "Type"
}
ComboBox {
id: holdingTypeComboBox
Layout.fillWidth: true
model: d.holdingTypesModel
textRole: "text"
valueRole: "value"
onActivated: root.type = currentValue
Component.onCompleted: currentIndex = indexOfValue(root.type)
}
}
RowLayout {
ColumnLayout {
Label {
Layout.fillWidth: true
text: d.ensLayout ? "Domain" : "Key"
}
ComboBox {
Layout.fillWidth: true
visible: !d.ensLayout
model: root.type === HoldingTypes.Type.Asset
? root.assetKeys : root.collectibleKeys
onActivated: root.key = currentText
Component.onCompleted: currentIndex = find(root.key)
}
TextField {
Layout.fillWidth: true
visible: d.ensLayout
text: root.key
onTextChanged: root.key = text
}
}
ColumnLayout {
visible: !d.ensLayout
Label {
Layout.fillWidth: true
text: "Amount"
}
TextField {
Layout.fillWidth: true
text: root.amountText
onTextChanged: root.amountText = text
}
}
}
MenuSeparator {
Layout.fillWidth: true
}
}

View File

@ -6,13 +6,21 @@ import Storybook 1.0
import Models 1.0
import StatusQ.Core.Utils 0.1
import AppLayouts.Chat.controls.community 1.0
Flickable {
id: root
property var model
property var assetKeys: []
property var collectibleKeys: []
QtObject {
id: d
property int newType
property string newKey
property string newName
property double newAmount
property string newImageSource
@ -54,45 +62,48 @@ Flickable {
Repeater {
model: holdingsListModel
CommunityPermissionsSettingItemEditor {
CommunityPermissionsHoldingItemEditor {
Layout.fillWidth: true
panelText: "Who holds [item " + model.index + "]"
name: model.name
icon: model.imageSource
type: model.type
key: model.key
amountText: model.amount
isAmountVisible: true
iconsModel: AssetsCollectiblesIconsModel {}
onNameChanged: model.name = name
onIconChanged: model.imageSource = icon
assetKeys: root.assetKeys
collectibleKeys: root.collectibleKeys
onTypeChanged: model.type = type
onKeyChanged: model.key = key
onAmountTextChanged: model.amount = parseFloat(amountText)
}
}
}
CommunityPermissionsSettingItemEditor {
panelText: "New holdings item"
name: d.newName
icon: d.newImageSource
CommunityPermissionsHoldingItemEditor {
panelText: "New holding item"
type: d.newType
key: d.newKey
amountText: d.newAmount
isAmountVisible: true
iconsModel: AssetsCollectiblesIconsModel {}
onNameChanged: d.newName = name
onIconChanged: d.newImageSource = icon
assetKeys: root.assetKeys
collectibleKeys: root.collectibleKeys
onTypeChanged: d.newType = type
onKeyChanged: d.newKey = key
onAmountTextChanged: d.newAmount = parseFloat(amountText)
}
Button {
enabled: d.newName && d.newAmount && d.newImageSource
enabled: d.newKey && (d.newAmount || d.newType === HoldingTypes.Type.Ens)
Layout.fillWidth: true
text: "Add new holding"
onClicked: {
model.holdingsListModel.append([{
type: 1,
key: d.newName,
name: d.newName,
amount: d.newAmount,
imageSource: d.newImageSource
}])
model.holdingsListModel.append({
type: d.newType,
key: d.newKey,
amount: d.newAmount
})
}
}

View File

@ -7,6 +7,8 @@ import AppLayouts.Chat.stores 1.0
import Storybook 1.0
import Models 1.0
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1
SplitView {
Logs { id: logs }
@ -30,6 +32,14 @@ SplitView {
id: mockedCommunity
permissionsModel: PermissionsModel.permissionsModel
readonly property var assetsModel: AssetsModel {
id: assetsModel
}
readonly property var collectiblesModel: CollectiblesModel {
id: collectiblesModel
}
function duplicatePermission(index) {
logs.logEvent("CommunitiesStore::duplicatePermission - index: " + index)
}
@ -76,6 +86,11 @@ SplitView {
CommunityPermissionsSettingsPanelEditor {
anchors.fill: parent
model: mockedCommunity.permissionsModel
assetKeys: ModelUtils.modelToArray(
assetsModel, ["key"]).map(asset => asset.key)
collectibleKeys: ModelUtils.modelToArray(
collectiblesModel, ["key"]).map(collectible => collectible.key)
}
}
}

View File

@ -41,10 +41,13 @@ Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
property bool isInvitationPending: true
property bool isJoinRequestRejected: false
property bool requiresRequest: false
property var communityHoldingsModel: PermissionsModel.shortPermissionsModel
property var viewOnlyHoldingsModel: PermissionsModel.shortPermissionsModel
property var viewAndPostHoldingsModel: PermissionsModel.shortPermissionsModel
property var moderateHoldingsModel: PermissionsModel.shortPermissionsModel
property var assetsModel: AssetsModel {}
property var collectiblesModel: CollectiblesModel {}
// Blur background:
property int membersCount: 184
@ -128,10 +131,13 @@ Nemo enim 😋 ipsam voluptatem quia voluptas sit aspernatur aut odit aut fugit,
requirementsMet: d.requirementsMet
isJoinRequestRejected: d.isJoinRequestRejected
requiresRequest: d.requiresRequest
communityHoldingsModel: d.communityHoldingsModel
viewOnlyHoldingsModel: d.viewOnlyHoldingsModel
viewAndPostHoldingsModel: d.viewAndPostHoldingsModel
moderateHoldingsModel: d.moderateHoldingsModel
assetsModel: d.assetsModel
collectiblesModel: d.collectiblesModel
onInfoButtonClicked: logs.logEvent("JoinCommunityView::onInfoButtonClicked()")
onAdHocChatButtonClicked: {

View File

@ -25,10 +25,14 @@ SplitView {
property bool isInvitationPending: false
property bool isJoinRequestRejected: false
property bool requiresRequest: false
property var communityHoldingsModel: PermissionsModel.shortPermissionsModel
property var viewOnlyHoldingsModel: PermissionsModel.shortPermissionsModel
property var viewAndPostHoldingsModel: PermissionsModel.shortPermissionsModel
property var moderateHoldingsModel: PermissionsModel.shortPermissionsModel
property var assetsModel: AssetsModel {}
property var collectiblesModel: CollectiblesModel {}
}
Logs { id: logs }
@ -67,6 +71,9 @@ SplitView {
JoinPermissionsOverlayPanel {
id: overlayPannel
assetsModel: d.assetsModel
collectiblesModel: d.collectiblesModel
anchors.centerIn: parent
joinCommunity: d.joinCommunity
requirementsMet: d.requirementsMet

View File

@ -1,4 +1,4 @@
import QtQuick 2.0
import QtQuick 2.15
import AppLayouts.Chat.controls.community 1.0

View File

@ -72,30 +72,21 @@ QtObject {
function createHoldingsModel1() {
return [
{
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Asset,
key: "SOCKS",
name: "SOCKS",
key: "socks",
amount: 1.2,
imageSource: ModelsData.assets.socks,
available: true
},
{
operator: OperatorsUtils.Operators.Or,
type: HoldingTypes.Type.Asset,
key: "ZRX",
name: "ZRX",
key: "zrx",
amount: 15,
imageSource: ModelsData.assets.zrx,
available: false
},
{
operator: OperatorsUtils.Operators.And,
type: HoldingTypes.Type.Collectible,
key: "Furbeard",
name: "Furbeard",
key: "Kitty1",
amount: 12,
imageSource: ModelsData.collectibles.kitty1,
available: true
}
]
@ -104,21 +95,15 @@ QtObject {
function createHoldingsModel2() {
return [
{
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Collectible,
key: "Happy Meow",
name: "Happy Meow",
key: "Kitty3",
amount: 50.25,
imageSource: ModelsData.collectibles.kitty3,
available: true
},
{
operator: OperatorsUtils.Operators.And,
type: HoldingTypes.Type.Collectible,
key: "AMP",
name: "AMP",
key: "Anniversary",
amount: 11,
imageSource: ModelsData.assets.amp,
available: false
}
]
@ -127,20 +112,14 @@ QtObject {
function createHoldingsModel3() {
return [
{
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Asset,
key: "uni",
imageSource: ModelsData.assets.uni,
name: "UNI",
key: "socks",
amount: 15,
available: true
},
{
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Asset,
key: "eth",
imageSource: ModelsData.assets.eth,
name: "ETH",
key: "zrx",
amount: 1,
available: false
}
@ -150,47 +129,32 @@ QtObject {
function createHoldingsModel4() {
return [
{
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Asset,
key: "uni",
imageSource: ModelsData.assets.uni,
name: "UNI",
key: "socks",
amount: 15,
available: true
},
{
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Asset,
key: "eth",
imageSource: ModelsData.assets.eth,
name: "ETH",
key: "zrx",
amount: 1,
available: false
},
{
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Asset,
key: "snt",
imageSource: ModelsData.assets.snt,
name: "SNT",
key: "1inch",
amount: 25000,
available: true
},
{
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Asset,
key: "uni",
imageSource: ModelsData.assets.dai,
name: "DAI",
key: "Aave",
amount: 100,
available: true
},
{
operator: OperatorsUtils.Operators.None,
type: HoldingTypes.Type.Asset,
key: "mana",
imageSource: ModelsData.assets.mana,
name: "MANA",
key: "Amp",
amount: 2,
available: true
}

View File

@ -9,14 +9,16 @@ import StatusQ.Controls 0.1
import AppLayouts.Chat.helpers 1.0
import AppLayouts.Chat.controls.community 1.0
import SortFilterProxyModel 0.2
import AppLayouts.Chat.views.communities 1.0
import utils 1.0
Control {
id: root
property var assetsModel
property var collectiblesModel
property var model
property string introText
@ -57,19 +59,18 @@ Control {
spacing: 18 // by design
Repeater {
model: SortFilterProxyModel {
model: HoldingsSelectionModel {
sourceModel: holdingsListModel
proxyRoles: ExpressionRole {
name: "text"
// Direct call for singleton function is not handled properly by SortFilterProxyModel that's why `holdingsTextFormat` is used instead.
expression: d.holdingsTextFormat(model.name, model.amount)
}
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
}
StatusListItemTag {
enabled: false
leftPadding: 2
title: text
title: model.text
asset.name: model.imageSource
asset.isImage: true
asset.bgColor: "transparent"

View File

@ -29,6 +29,9 @@ Control {
property var moderateHoldingsModel
property bool showOnlyPanels: false
property var assetsModel
property var collectiblesModel
signal revealAddressClicked
signal invitationPendingClicked
@ -61,27 +64,31 @@ Control {
spacing: 32 // default by design
contentItem: ColumnLayout {
id: column
spacing: root.spacing
HoldingsListPanel {
component CustomHoldingsListPanel: HoldingsListPanel {
Layout.fillWidth: true
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
spacing: root.spacing
}
CustomHoldingsListPanel {
visible: root.joinCommunity && root.communityHoldingsModel
introText: qsTr("To join <b>%1</b> you need to prove that you hold").arg(root.communityName)
model: root.communityHoldingsModel
}
HoldingsListPanel {
Layout.fillWidth: true
spacing: root.spacing
CustomHoldingsListPanel {
visible: !root.joinCommunity && !!root.viewOnlyHoldingsModel
introText: qsTr("To only view the <b>%1</b> channel you need to hold").arg(root.channelName)
model: root.viewOnlyHoldingsModel
}
HoldingsListPanel {
Layout.fillWidth: true
spacing: root.spacing
CustomHoldingsListPanel {
visible: !root.joinCommunity && !!root.viewAndPostHoldingsModel
introText: qsTr("To view and post in the <b>%1</b> channel you need to hold").arg(root.channelName)
model: root.viewAndPostHoldingsModel
@ -90,7 +97,7 @@ Control {
HoldingsListPanel {
Layout.fillWidth: true
spacing: root.spacing
visible: !root.joinCommunity && !!root.moderateHoldingsModel
visible: !root.joinCommunity && !!root.moderateHoldings
introText: qsTr("To moderate in the <b>%1</b> channel you need to hold").arg(root.channelName)
model: root.moderateHoldingsModel
}

View File

@ -147,9 +147,7 @@ QtObject {
permission.holdingsListModel.push({
type: entry.type,
key: entry.key,
name: entry.name,
amount: entry.amount,
imageSource: entry.imageSource
amount: entry.amount
})
}

View File

@ -128,8 +128,7 @@ StatusScrollView {
d.dirtyValues.holdingsModel.clear()
d.dirtyValues.holdingsModel.append(
ModelUtils.modelToArray(root.holdingsModel,
["type", "key", "name", "amount", "imageSource", "shortName"]))
ModelUtils.modelToArray(root.holdingsModel, ["type", "key", "amount"]))
// Permissions:
d.dirtyValues.permissionType = root.permissionType
@ -152,10 +151,6 @@ StatusScrollView {
// Is private permission
d.dirtyValues.isPrivate = root.isPrivate
}
function holdingsTextFormat(type, name, amount) {
return CommunityPermissionsHelpers.setHoldingsTextFormat(type, name, amount)
}
}
contentWidth: mainLayout.width
@ -187,24 +182,12 @@ StatusScrollView {
asset.height: 28
asset.width: asset.height
addButton.visible: itemsModel.count < d.maxHoldingsItems
itemsModel: SortFilterProxyModel {
itemsModel: HoldingsSelectionModel {
sourceModel: d.dirtyValues.holdingsModel
proxyRoles: [
ExpressionRole {
name: "text"
// Direct call for singleton function is not handled properly by SortFilterProxyModel that's why `holdingsTextFormat` is used instead.
expression: d.holdingsTextFormat(model.type, model.name, model.amount)
},
ExpressionRole {
name: "operator"
// Direct call for singleton enum is not handled properly by SortFilterProxyModel.
readonly property int none: OperatorsUtils.Operators.None
expression: none
}
]
assetsModel: root.store.assetsModel
collectiblesModel: root.store.collectiblesModel
}
HoldingsDropdown {
@ -214,10 +197,8 @@ StatusScrollView {
function addItem(type, item, amount) {
const key = item.key
const name = item.shortName ? item.shortName : item.name
const imageSource = item.iconSource.toString()
d.dirtyValues.holdingsModel.append({ type, key, name, amount, imageSource })
d.dirtyValues.holdingsModel.append({ type, key, amount })
}
function prepareUpdateIndex(key) {
@ -255,40 +236,32 @@ StatusScrollView {
}
onAddEns: {
const key = "ENS_" + domain
const icon = Style.svg("profile/ensUsernames")
d.dirtyValues.holdingsModel.append({type: HoldingTypes.Type.Ens, key, name: domain, amount: 1, imageSource: icon })
d.dirtyValues.holdingsModel.append(
{ type: HoldingTypes.Type.Ens, key: domain, amount: 1 })
dropdown.close()
}
onUpdateAsset: {
const itemIndex = prepareUpdateIndex(key)
const modelItem = CommunityPermissionsHelpers.getTokenByKey(store.assetsModel, key)
const name = modelItem.shortName ? modelItem.shortName : modelItem.name
const imageSource = modelItem.iconSource.toString()
d.dirtyValues.holdingsModel.set(itemIndex, { type: HoldingTypes.Type.Asset, key, name, amount, imageSource })
d.dirtyValues.holdingsModel.set(itemIndex,
{ type: HoldingTypes.Type.Asset, key, amount })
dropdown.close()
}
onUpdateCollectible: {
const itemIndex = prepareUpdateIndex(key)
const modelItem = CommunityPermissionsHelpers.getTokenByKey(store.collectiblesModel, key)
const name = modelItem.name
const imageSource = modelItem.iconSource.toString()
d.dirtyValues.holdingsModel.set(itemIndex, { type: HoldingTypes.Type.Collectible, key, name, amount, imageSource })
d.dirtyValues.holdingsModel.set(itemIndex,
{ type: HoldingTypes.Type.Collectible, key, amount })
dropdown.close()
}
onUpdateEns: {
const key = "ENS_" + domain
const icon = Style.svg("profile/ensUsernames")
d.dirtyValues.holdingsModel.set(tokensSelector.editedIndex, { type: HoldingTypes.Type.Ens, key, name: domain, amount: 1, imageSource: icon })
d.dirtyValues.holdingsModel.set(tokensSelector.editedIndex,
{ type: HoldingTypes.Type.Ens, key: domain, amount: 1 })
dropdown.close()
}
@ -327,7 +300,7 @@ StatusScrollView {
dropdown.collectibleAmount = modelItem.amount
break
case HoldingTypes.Type.Ens:
dropdown.ensDomainName = modelItem.name
dropdown.ensDomainName = modelItem.key
break
default:
console.warn("Unsupported holdings type.")

View File

@ -24,11 +24,8 @@ StatusScrollView {
QtObject {
id: d
property int permissionIndexToRemove
function holdingsTextFormat(type, name, amount) {
return CommunityPermissionsHelpers.setHoldingsTextFormat(type, name, amount)
}
property int permissionIndexToRemove
}
contentWidth: mainLayout.width
@ -59,15 +56,10 @@ StatusScrollView {
delegate: PermissionItem {
Layout.preferredWidth: root.viewWidth
holdingsListModel: SortFilterProxyModel {
holdingsListModel: HoldingsSelectionModel {
sourceModel: model.holdingsListModel
proxyRoles: ExpressionRole {
name: "text"
// Direct call for singleton function is not handled properly
// by SortFilterProxyModel that's why `holdingsTextFormat` is used instead.
expression: d.holdingsTextFormat(model.type, model.name, model.amount)
}
assetsModel: store.assetsModel
collectiblesModel: store.collectiblesModel
}
permissionType: model.permissionType
@ -77,6 +69,7 @@ StatusScrollView {
sourceModel: model.channelsListModel
proxyRoles: [
ExpressionRole {
name: "imageSource"

View File

@ -0,0 +1,67 @@
import SortFilterProxyModel 0.2
import AppLayouts.Chat.helpers 1.0
import AppLayouts.Chat.panels.communities 1.0
import AppLayouts.Chat.controls.community 1.0
import StatusQ.Core.Utils 0.1
import utils 1.0
SortFilterProxyModel {
property var assetsModel
property var collectiblesModel
proxyRoles: [
ExpressionRole {
name: "text"
function getName(type, key) {
if (type === HoldingTypes.Type.Ens)
return key
const model = type === HoldingTypes.Type.Asset
? assetsModel
: collectiblesModel
const item = CommunityPermissionsHelpers.getTokenByKey(model, key)
return item ? item.name : ""
}
function getText(type, key, amount) {
const name = getName(type, key)
return CommunityPermissionsHelpers.setHoldingsTextFormat(
type, name, amount)
}
// Direct call for singleton function is not handled properly by
// SortFilterProxyModel that's why helper function is used instead.
expression: getText(model.type, model.key, model.amount)
},
ExpressionRole {
name: "imageSource"
function getIcon(type, key) {
if (type === HoldingTypes.Type.Ens)
return Style.svg("profile/ensUsernames")
const model = type === HoldingTypes.Type.Asset
? assetsModel : collectiblesModel
return CommunityPermissionsHelpers.getTokenIconByKey(model, key)
}
expression: getIcon(model.type, model.key)
},
ExpressionRole {
name: "operator"
// Direct call for singleton enum is not handled properly by SortFilterProxyModel.
readonly property int none: OperatorsUtils.Operators.None
expression: none
}
]
}

View File

@ -36,10 +36,13 @@ StatusSectionLayout {
property bool requirementsMet: true
property bool isJoinRequestRejected: false
property bool requiresRequest: false
property var communityHoldingsModel
property var viewOnlyHoldingsModel
property var viewAndPostHoldingsModel
property var moderateHoldingsModel
property var assetsModel
property var collectiblesModel
// Blur view properties:
property int membersCount
@ -271,9 +274,12 @@ StatusSectionLayout {
communityName: root.name
communityHoldingsModel: root.communityHoldingsModel
channelName: root.channelName
viewOnlyHoldingsModel: root.viewOnlyHoldingsModel
viewAndPostHoldingsModel: root.viewAndPostHoldingsModel
moderateHoldingsModel: root.moderateHoldingsModel
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
onRevealAddressClicked: root.revealAddressClicked()
onInvitationPendingClicked: root.invitationPendingClicked()

View File

@ -1,4 +1,6 @@
CommunityNewPermissionView 1.0 CommunityNewPermissionView.qml
CommunityPermissionsView 1.0 CommunityPermissionsView.qml
CommunityWelcomePermissionsView 1.0 CommunityWelcomePermissionsView.qml
HoldingsSelectionModel 1.0 HoldingsSelectionModel.qml
JoinCommunityView 1.0 JoinCommunityView.qml
CommunityWelcomeSettingsView 1.0 CommunityWelcomeSettingsView.qml