2023-04-04 14:10:37 +00:00
|
|
|
|
import QtQuick 2.15
|
|
|
|
|
import QtQuick.Layouts 1.15
|
|
|
|
|
import QtQml 2.15
|
2022-06-09 15:27:14 +00:00
|
|
|
|
|
fix(permissions): fix hang when all channel perm check return (#14259)
* fix(permissions): fix hang when all channel perm check return
Fixes #14234
The problem was that we updated **all** the models from **all** the channels of a community each time the channel requirement checks returned.
The fix is to first of all, make sure we don't call that check too often. It sometimes got called twice in a row by accident.
The other better fix is to check if anything actually changed before updating. This solves the issue almost entirely. Since the permissions almost never change, the updates now take only a second.
* fix(permisisons): never run permission checks for privileged users
Also fixes #14234 but for admins, TMs and Owners.
Admins+ were still getting the hang, because the permission checks always returned something different than the models, because the models knew that admins have access to everything, but the permission check was running as if it were a normal user (I think, un-tested).
Anyway, the solution is more simple, we never need to run the permission checks on admins+, because they always have access to everything!
* fix(Communities): prevent channels model from emitting unnecessary signals
Closes: #14274
* chore(Communities): improve channels metadata lookup performance
ChannelsSelectionModel is removed, replaced with plain LeftJoinModel.
Transformations of left-side model are done in a single place, not in
every delegate making the join.
* only call update functions when there is something to update + move permission model creation when needed
---------
Co-authored-by: Michał Cieślak <michalcieslak@status.im>
2024-04-04 15:26:44 +00:00
|
|
|
|
import StatusQ 0.1
|
2022-06-09 15:27:14 +00:00
|
|
|
|
import StatusQ.Core 0.1
|
|
|
|
|
import StatusQ.Core.Theme 0.1
|
|
|
|
|
import StatusQ.Components 0.1
|
|
|
|
|
import StatusQ.Controls 0.1
|
2022-11-25 17:35:30 +00:00
|
|
|
|
import StatusQ.Core.Utils 0.1
|
2022-06-09 15:27:14 +00:00
|
|
|
|
|
|
|
|
|
import utils 1.0
|
|
|
|
|
import shared.panels 1.0
|
|
|
|
|
|
2023-06-29 22:05:46 +00:00
|
|
|
|
import AppLayouts.Communities.controls 1.0
|
2023-06-23 06:17:04 +00:00
|
|
|
|
import AppLayouts.Communities.helpers 1.0
|
|
|
|
|
import AppLayouts.Communities.panels 1.0
|
2023-06-29 22:05:46 +00:00
|
|
|
|
import AppLayouts.Communities.popups 1.0
|
2022-06-09 15:27:14 +00:00
|
|
|
|
|
2022-12-13 13:09:38 +00:00
|
|
|
|
StatusScrollView {
|
2022-06-09 15:27:14 +00:00
|
|
|
|
id: root
|
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
required property var assetsModel
|
|
|
|
|
required property var collectiblesModel
|
|
|
|
|
required property var channelsModel
|
|
|
|
|
|
2023-06-21 14:20:39 +00:00
|
|
|
|
// id, name, image, color, owner properties expected
|
2023-03-07 08:47:04 +00:00
|
|
|
|
required property var communityDetails
|
|
|
|
|
|
2024-02-07 10:30:46 +00:00
|
|
|
|
readonly property bool saveEnabled: root.isFullyFilled
|
|
|
|
|
&& !root.permissionDuplicated
|
2024-03-18 17:28:09 +00:00
|
|
|
|
&& (isEditState ? !root.permissionTypeLimitExceeded : !root.permissionTypeLimitReached)
|
2024-02-07 10:30:46 +00:00
|
|
|
|
|
2022-11-25 17:35:30 +00:00
|
|
|
|
property int viewWidth: 560 // by design
|
2022-11-24 16:23:54 +00:00
|
|
|
|
property bool isEditState: false
|
2023-02-10 22:40:07 +00:00
|
|
|
|
|
2023-02-11 19:03:57 +00:00
|
|
|
|
readonly property bool dirty:
|
2023-04-04 14:10:37 +00:00
|
|
|
|
root.holdingsRequired !== d.dirtyValues.holdingsRequired ||
|
|
|
|
|
(d.dirtyValues.holdingsRequired && !holdingsModelComparator.equal) ||
|
2023-02-11 19:03:57 +00:00
|
|
|
|
!channelsModelComparator.equal ||
|
|
|
|
|
root.isPrivate !== d.dirtyValues.isPrivate ||
|
|
|
|
|
root.permissionType !== d.dirtyValues.permissionType
|
2022-11-24 16:23:54 +00:00
|
|
|
|
|
2023-02-14 23:53:01 +00:00
|
|
|
|
readonly property alias dirtyValues: d.dirtyValues
|
|
|
|
|
|
2024-02-06 09:31:36 +00:00
|
|
|
|
readonly property bool isFullyFilled: (dirtyValues.selectedHoldingsModel.count > 0 || !whoHoldsSwitch.checked) &&
|
2023-02-14 23:53:01 +00:00
|
|
|
|
dirtyValues.permissionType !== PermissionTypes.Type.None &&
|
2024-02-06 09:31:36 +00:00
|
|
|
|
(d.isCommunityPermission || !showChannelSelector || dirtyValues.selectedChannelsModel.count > 0)
|
2023-02-14 23:53:01 +00:00
|
|
|
|
|
2023-02-11 19:03:57 +00:00
|
|
|
|
property int permissionType: PermissionTypes.Type.None
|
|
|
|
|
property bool isPrivate: false
|
2023-04-04 14:10:37 +00:00
|
|
|
|
property bool holdingsRequired: true
|
2024-02-06 09:31:36 +00:00
|
|
|
|
property bool showChannelSelector: true
|
2023-02-08 10:13:16 +00:00
|
|
|
|
|
2023-01-12 11:31:08 +00:00
|
|
|
|
// roles: type, key, name, amount, imageSource
|
2023-03-07 08:47:04 +00:00
|
|
|
|
property var selectedHoldingsModel: ListModel {}
|
2022-11-24 16:23:54 +00:00
|
|
|
|
|
2023-02-28 12:44:46 +00:00
|
|
|
|
// roles: itemId, text, icon, emoji, color, colorId
|
2023-03-07 08:47:04 +00:00
|
|
|
|
property var selectedChannelsModel: ListModel {}
|
2022-11-24 16:23:54 +00:00
|
|
|
|
|
2023-03-02 15:58:26 +00:00
|
|
|
|
property bool permissionDuplicated: false
|
|
|
|
|
property bool permissionTypeLimitReached: false
|
2024-03-18 17:28:09 +00:00
|
|
|
|
property bool permissionTypeLimitExceeded
|
2023-02-14 20:39:18 +00:00
|
|
|
|
|
2023-02-08 23:22:57 +00:00
|
|
|
|
signal createPermissionClicked
|
2023-07-05 21:43:15 +00:00
|
|
|
|
signal navigateToMintTokenSettings(bool isAssetType)
|
2023-02-08 23:22:57 +00:00
|
|
|
|
|
|
|
|
|
function resetChanges() {
|
|
|
|
|
d.loadInitValues()
|
|
|
|
|
}
|
2022-08-23 08:46:37 +00:00
|
|
|
|
|
2023-02-09 22:31:34 +00:00
|
|
|
|
ModelsComparator {
|
|
|
|
|
id: holdingsModelComparator
|
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
modelA: root.dirtyValues.selectedHoldingsModel
|
|
|
|
|
modelB: root.selectedHoldingsModel
|
2023-02-09 22:31:34 +00:00
|
|
|
|
|
2023-02-21 19:54:38 +00:00
|
|
|
|
roles: ["key", "amount"]
|
2023-02-09 22:31:34 +00:00
|
|
|
|
mode: ModelsComparator.CompareMode.Set
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ModelsComparator {
|
|
|
|
|
id: channelsModelComparator
|
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
modelA: root.dirtyValues.selectedChannelsModel
|
|
|
|
|
modelB: root.selectedChannelsModel
|
2023-02-09 22:31:34 +00:00
|
|
|
|
|
2023-07-04 07:37:57 +00:00
|
|
|
|
roles: ["key"]
|
2023-02-09 22:31:34 +00:00
|
|
|
|
mode: ModelsComparator.CompareMode.Set
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 15:27:14 +00:00
|
|
|
|
QtObject {
|
|
|
|
|
id: d
|
2022-12-20 00:54:50 +00:00
|
|
|
|
|
2023-01-19 17:36:06 +00:00
|
|
|
|
readonly property int maxHoldingsItems: 5
|
|
|
|
|
|
2022-12-20 00:54:50 +00:00
|
|
|
|
readonly property int dropdownHorizontalOffset: 4
|
|
|
|
|
readonly property int dropdownVerticalOffset: 1
|
|
|
|
|
|
2023-01-20 11:15:05 +00:00
|
|
|
|
readonly property bool isCommunityPermission:
|
2023-02-14 23:53:01 +00:00
|
|
|
|
PermissionTypes.isCommunityPermission(dirtyValues.permissionType)
|
2023-01-20 11:22:04 +00:00
|
|
|
|
|
2023-01-20 11:15:05 +00:00
|
|
|
|
onIsCommunityPermissionChanged: {
|
|
|
|
|
if (isCommunityPermission) {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedChannelsModel.clear()
|
2023-01-20 11:15:05 +00:00
|
|
|
|
inSelector.wholeCommunitySelected = true
|
2023-03-13 10:02:41 +00:00
|
|
|
|
inSelector.model = inModelCommunity
|
2023-01-20 11:15:05 +00:00
|
|
|
|
} else {
|
2023-03-13 10:02:41 +00:00
|
|
|
|
inSelector.model = 0
|
2023-01-20 11:15:05 +00:00
|
|
|
|
inSelector.wholeCommunitySelected = false
|
2023-03-13 10:02:41 +00:00
|
|
|
|
inSelector.model = channelsSelectionModel
|
2023-01-20 11:15:05 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-08 23:22:57 +00:00
|
|
|
|
readonly property QtObject dirtyValues: QtObject {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
readonly property ListModel selectedHoldingsModel: ListModel {}
|
|
|
|
|
readonly property ListModel selectedChannelsModel: ListModel {}
|
2023-01-27 09:22:04 +00:00
|
|
|
|
|
2023-02-11 19:03:57 +00:00
|
|
|
|
property int permissionType: PermissionTypes.Type.None
|
2023-02-08 12:39:47 +00:00
|
|
|
|
property bool isPrivate: false
|
2023-04-04 14:10:37 +00:00
|
|
|
|
property bool holdingsRequired: true
|
2022-11-24 16:23:54 +00:00
|
|
|
|
|
2023-02-11 19:03:57 +00:00
|
|
|
|
Binding on isPrivate {
|
2024-03-18 17:28:09 +00:00
|
|
|
|
value: d.dirtyValues.permissionType === PermissionTypes.Type.Admin
|
2023-02-11 19:03:57 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-09 16:18:17 +00:00
|
|
|
|
function getHoldingIndex(key) {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
return ModelUtils.indexOf(selectedHoldingsModel, "key", key)
|
2023-01-24 21:49:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getTokenKeysAndAmounts() {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
return ModelUtils.modelToArray(selectedHoldingsModel, ["type", "key", "amount"])
|
2023-11-07 22:45:47 +00:00
|
|
|
|
.filter(item => item.type !== Constants.TokenType.ENS)
|
2023-02-09 16:18:17 +00:00
|
|
|
|
.map(item => ({ key: item.key, amount: item.amount }))
|
2023-01-24 21:49:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getEnsNames() {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
return ModelUtils.modelToArray(selectedHoldingsModel, ["type", "name"])
|
2023-11-07 22:45:47 +00:00
|
|
|
|
.filter(item => item.type === Constants.TokenType.ENS)
|
2023-02-09 16:18:17 +00:00
|
|
|
|
.map(item => item.name)
|
2023-01-24 21:49:07 +00:00
|
|
|
|
}
|
2022-11-24 16:23:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function loadInitValues() {
|
|
|
|
|
// Holdings:
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedHoldingsModel.clear()
|
|
|
|
|
d.dirtyValues.selectedHoldingsModel.append(
|
|
|
|
|
ModelUtils.modelToArray(root.selectedHoldingsModel,
|
|
|
|
|
["type", "key", "amount"]))
|
2022-11-24 16:23:54 +00:00
|
|
|
|
|
|
|
|
|
// Permissions:
|
2023-02-11 19:03:57 +00:00
|
|
|
|
d.dirtyValues.permissionType = root.permissionType
|
2023-01-27 09:22:04 +00:00
|
|
|
|
|
|
|
|
|
// Channels
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedChannelsModel.clear()
|
|
|
|
|
d.dirtyValues.selectedChannelsModel.append(
|
|
|
|
|
ModelUtils.modelToArray(root.selectedChannelsModel, ["key"]))
|
2023-01-27 09:22:04 +00:00
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
if (root.selectedChannelsModel &&
|
|
|
|
|
(root.selectedChannelsModel.rowCount()
|
|
|
|
|
|| d.dirtyValues.permissionType === PermissionTypes.Type.None)) {
|
2023-01-27 09:22:04 +00:00
|
|
|
|
inSelector.wholeCommunitySelected = false
|
2023-03-13 10:02:41 +00:00
|
|
|
|
inSelector.model = channelsSelectionModel
|
2023-01-27 09:22:04 +00:00
|
|
|
|
} else {
|
|
|
|
|
inSelector.wholeCommunitySelected = true
|
2023-03-13 10:02:41 +00:00
|
|
|
|
inSelector.model = inModelCommunity
|
2023-01-27 09:22:04 +00:00
|
|
|
|
}
|
2022-11-24 16:23:54 +00:00
|
|
|
|
|
|
|
|
|
// Is private permission
|
2023-02-08 12:39:47 +00:00
|
|
|
|
d.dirtyValues.isPrivate = root.isPrivate
|
2023-04-04 14:10:37 +00:00
|
|
|
|
|
|
|
|
|
// Are holdings required
|
|
|
|
|
d.dirtyValues.holdingsRequired = root.holdingsRequired
|
2022-11-24 16:23:54 +00:00
|
|
|
|
}
|
2022-06-09 15:27:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-02-14 20:39:18 +00:00
|
|
|
|
onPermissionTypeChanged: Qt.callLater(() => d.loadInitValues())
|
2023-06-20 06:55:16 +00:00
|
|
|
|
contentWidth: mainLayout.width
|
|
|
|
|
contentHeight: mainLayout.height
|
2023-08-10 12:23:59 +00:00
|
|
|
|
|
2023-04-25 20:09:00 +00:00
|
|
|
|
SequenceColumnLayout {
|
2022-06-09 15:27:14 +00:00
|
|
|
|
id: mainLayout
|
2023-01-27 09:22:04 +00:00
|
|
|
|
|
2023-04-25 20:09:00 +00:00
|
|
|
|
width: root.viewWidth
|
|
|
|
|
title: qsTr("Anyone")
|
2023-01-27 09:22:04 +00:00
|
|
|
|
|
2022-06-09 15:27:14 +00:00
|
|
|
|
StatusItemSelector {
|
|
|
|
|
id: tokensSelector
|
2023-01-18 19:54:14 +00:00
|
|
|
|
|
2023-01-24 21:49:07 +00:00
|
|
|
|
property int editedIndex: -1
|
2023-01-18 19:54:14 +00:00
|
|
|
|
|
2022-06-09 15:27:14 +00:00
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
icon: Style.svg("contact_verified")
|
|
|
|
|
title: qsTr("Who holds")
|
2023-03-13 10:02:41 +00:00
|
|
|
|
placeholderText: qsTr("Example: 10 SNT")
|
2022-12-21 13:59:43 +00:00
|
|
|
|
tagLeftPadding: 2
|
|
|
|
|
asset.height: 28
|
|
|
|
|
asset.width: asset.height
|
2023-04-04 14:10:37 +00:00
|
|
|
|
addButton.visible: count < d.maxHoldingsItems &&
|
|
|
|
|
whoHoldsSwitch.checked
|
2022-09-07 09:40:25 +00:00
|
|
|
|
|
2023-03-13 10:02:41 +00:00
|
|
|
|
model: HoldingsSelectionModel {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
sourceModel: d.dirtyValues.selectedHoldingsModel
|
2023-01-27 09:22:04 +00:00
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
assetsModel: root.assetsModel
|
|
|
|
|
collectiblesModel: root.collectiblesModel
|
2022-09-07 09:40:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-04-04 14:10:37 +00:00
|
|
|
|
label.enabled: whoHoldsSwitch.checked
|
|
|
|
|
placeholderItem.visible: count === 0 && whoHoldsSwitch.checked
|
|
|
|
|
|
|
|
|
|
Binding on model {
|
|
|
|
|
when: !whoHoldsSwitch.checked
|
|
|
|
|
value: 0
|
|
|
|
|
restoreMode: Binding.RestoreBindingOrValue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Binding on bottomPadding {
|
|
|
|
|
when: !whoHoldsSwitch.checked
|
|
|
|
|
value: 0
|
|
|
|
|
restoreMode: Binding.RestoreBindingOrValue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
children: StatusSwitch {
|
|
|
|
|
id: whoHoldsSwitch
|
|
|
|
|
|
|
|
|
|
padding: 0
|
|
|
|
|
anchors.right: parent.right
|
|
|
|
|
anchors.top: parent.top
|
|
|
|
|
anchors.rightMargin: 12
|
|
|
|
|
anchors.topMargin: 10
|
|
|
|
|
|
|
|
|
|
checked: d.dirtyValues.holdingsRequired
|
|
|
|
|
onToggled: d.dirtyValues.holdingsRequired = checked
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-07 09:40:25 +00:00
|
|
|
|
HoldingsDropdown {
|
2022-06-09 15:27:14 +00:00
|
|
|
|
id: dropdown
|
2023-01-27 09:22:04 +00:00
|
|
|
|
|
2023-06-21 14:20:39 +00:00
|
|
|
|
communityId: root.communityDetails.id
|
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
assetsModel: root.assetsModel
|
|
|
|
|
collectiblesModel: root.collectiblesModel
|
2023-08-08 19:32:46 +00:00
|
|
|
|
showTokenAmount: false
|
2022-09-07 09:40:25 +00:00
|
|
|
|
|
2023-01-12 11:31:08 +00:00
|
|
|
|
function addItem(type, item, amount) {
|
2022-09-07 09:40:25 +00:00
|
|
|
|
const key = item.key
|
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedHoldingsModel.append(
|
2024-02-13 10:24:24 +00:00
|
|
|
|
{ type, key, amount })
|
2022-09-07 09:40:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-24 21:49:07 +00:00
|
|
|
|
function prepareUpdateIndex(key) {
|
|
|
|
|
const itemIndex = tokensSelector.editedIndex
|
2023-02-09 16:18:17 +00:00
|
|
|
|
const existingIndex = d.dirtyValues.getHoldingIndex(key)
|
2023-01-24 21:49:07 +00:00
|
|
|
|
|
|
|
|
|
if (itemIndex !== -1 && existingIndex !== -1 && itemIndex !== existingIndex) {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
const previousKey = d.dirtyValues.selectedHoldingsModel.get(itemIndex).key
|
|
|
|
|
d.dirtyValues.selectedHoldingsModel.remove(existingIndex)
|
2023-02-09 16:18:17 +00:00
|
|
|
|
return d.dirtyValues.getHoldingIndex(previousKey)
|
2023-01-24 21:49:07 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (itemIndex === -1) {
|
|
|
|
|
return existingIndex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return itemIndex
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onOpened: {
|
|
|
|
|
usedTokens = d.dirtyValues.getTokenKeysAndAmounts()
|
|
|
|
|
usedEnsNames = d.dirtyValues.getEnsNames().filter(item => item !== ensDomainName)
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:18:52 +00:00
|
|
|
|
onAddAsset: {
|
2023-06-26 11:48:45 +00:00
|
|
|
|
const modelItem = PermissionsHelpers.getTokenByKey(
|
2023-03-07 08:47:04 +00:00
|
|
|
|
root.assetsModel, key)
|
2023-08-10 12:23:59 +00:00
|
|
|
|
|
2024-04-04 08:45:25 +00:00
|
|
|
|
addItem(Constants.TokenType.ERC20, modelItem, AmountsArithmetic.fromNumber(amount, modelItem.decimals).toFixed())
|
2022-09-07 09:40:25 +00:00
|
|
|
|
dropdown.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onAddCollectible: {
|
2023-06-26 11:48:45 +00:00
|
|
|
|
const modelItem = PermissionsHelpers.getTokenByKey(
|
2023-03-07 08:47:04 +00:00
|
|
|
|
root.collectiblesModel, key)
|
2023-08-10 12:23:59 +00:00
|
|
|
|
|
2024-02-13 10:24:24 +00:00
|
|
|
|
addItem(Constants.TokenType.ERC721, modelItem, String(amount))
|
2022-09-07 09:40:25 +00:00
|
|
|
|
dropdown.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onAddEns: {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedHoldingsModel.append(
|
2024-02-13 10:24:24 +00:00
|
|
|
|
{ type: Constants.TokenType.ENS, key: domain, amount: "1" })
|
2022-09-07 09:40:25 +00:00
|
|
|
|
dropdown.close()
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-12 13:18:52 +00:00
|
|
|
|
onUpdateAsset: {
|
2023-01-24 21:49:07 +00:00
|
|
|
|
const itemIndex = prepareUpdateIndex(key)
|
2023-06-26 11:48:45 +00:00
|
|
|
|
const modelItem = PermissionsHelpers.getTokenByKey(root.assetsModel, key)
|
2022-09-07 09:40:25 +00:00
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedHoldingsModel.set(
|
2024-04-04 08:45:25 +00:00
|
|
|
|
itemIndex, { type: Constants.TokenType.ERC20, key, amount: AmountsArithmetic.fromNumber(amount, modelItem.decimals).toFixed() })
|
2022-06-09 15:27:14 +00:00
|
|
|
|
dropdown.close()
|
|
|
|
|
}
|
2022-09-07 09:40:25 +00:00
|
|
|
|
|
|
|
|
|
onUpdateCollectible: {
|
2023-01-24 21:49:07 +00:00
|
|
|
|
const itemIndex = prepareUpdateIndex(key)
|
2023-06-26 11:48:45 +00:00
|
|
|
|
const modelItem = PermissionsHelpers.getTokenByKey(
|
2023-03-07 08:47:04 +00:00
|
|
|
|
root.collectiblesModel, key)
|
2022-09-07 09:40:25 +00:00
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedHoldingsModel.set(
|
|
|
|
|
itemIndex,
|
2024-02-13 10:24:24 +00:00
|
|
|
|
{ type: Constants.TokenType.ERC721, key, amount: String(amount) })
|
2022-09-07 09:40:25 +00:00
|
|
|
|
dropdown.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onUpdateEns: {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedHoldingsModel.set(
|
|
|
|
|
tokensSelector.editedIndex,
|
2024-02-13 10:24:24 +00:00
|
|
|
|
{ type: Constants.TokenType.ENS, key: domain, amount: "1" })
|
2022-09-07 09:40:25 +00:00
|
|
|
|
dropdown.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onRemoveClicked: {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedHoldingsModel.remove(tokensSelector.editedIndex)
|
2022-09-07 09:40:25 +00:00
|
|
|
|
dropdown.close()
|
|
|
|
|
}
|
2023-03-24 12:41:46 +00:00
|
|
|
|
|
|
|
|
|
onNavigateToMintTokenSettings: {
|
2023-07-05 21:43:15 +00:00
|
|
|
|
root.navigateToMintTokenSettings(isAssetType)
|
2023-03-24 12:41:46 +00:00
|
|
|
|
close()
|
|
|
|
|
}
|
2022-09-07 09:40:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addButton.onClicked: {
|
|
|
|
|
dropdown.parent = tokensSelector.addButton
|
2022-12-20 00:54:50 +00:00
|
|
|
|
dropdown.x = tokensSelector.addButton.width + d.dropdownHorizontalOffset
|
2022-09-07 09:40:25 +00:00
|
|
|
|
dropdown.y = 0
|
2023-01-18 19:54:14 +00:00
|
|
|
|
dropdown.open()
|
2023-01-24 21:49:07 +00:00
|
|
|
|
|
|
|
|
|
editedIndex = -1
|
2022-09-07 09:40:25 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onItemClicked: {
|
|
|
|
|
if (mouse.button !== Qt.LeftButton)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
dropdown.parent = item
|
2022-12-20 00:54:50 +00:00
|
|
|
|
dropdown.x = mouse.x + d.dropdownHorizontalOffset
|
|
|
|
|
dropdown.y = d.dropdownVerticalOffset
|
2022-09-07 09:40:25 +00:00
|
|
|
|
|
2023-03-13 10:02:41 +00:00
|
|
|
|
const modelItem = tokensSelector.model.get(index)
|
2022-09-07 09:40:25 +00:00
|
|
|
|
|
|
|
|
|
switch(modelItem.type) {
|
2023-11-07 22:45:47 +00:00
|
|
|
|
case Constants.TokenType.ERC20:
|
2023-01-12 13:18:52 +00:00
|
|
|
|
dropdown.assetKey = modelItem.key
|
2024-02-13 10:24:24 +00:00
|
|
|
|
const decimals = PermissionsHelpers.getTokenByKey(root.assetsModel, modelItem.key).decimals
|
|
|
|
|
dropdown.assetAmount = AmountsArithmetic.toNumber(modelItem.amount, decimals)
|
2022-09-07 09:40:25 +00:00
|
|
|
|
break
|
2023-11-07 22:45:47 +00:00
|
|
|
|
case Constants.TokenType.ERC721:
|
2022-09-07 09:40:25 +00:00
|
|
|
|
dropdown.collectibleKey = modelItem.key
|
|
|
|
|
dropdown.collectibleAmount = modelItem.amount
|
|
|
|
|
break
|
2023-11-07 22:45:47 +00:00
|
|
|
|
case Constants.TokenType.ENS:
|
2023-02-13 10:40:13 +00:00
|
|
|
|
dropdown.ensDomainName = modelItem.key
|
2022-09-07 09:40:25 +00:00
|
|
|
|
break
|
|
|
|
|
default:
|
|
|
|
|
console.warn("Unsupported holdings type.")
|
|
|
|
|
}
|
|
|
|
|
|
2022-09-15 10:12:38 +00:00
|
|
|
|
dropdown.setActiveTab(modelItem.type)
|
2023-01-18 19:54:14 +00:00
|
|
|
|
dropdown.openUpdateFlow()
|
2022-09-15 10:12:38 +00:00
|
|
|
|
|
2022-09-07 09:40:25 +00:00
|
|
|
|
editedIndex = index
|
2022-06-09 15:27:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2023-04-25 20:09:00 +00:00
|
|
|
|
|
|
|
|
|
SequenceColumnLayout.Separator {}
|
|
|
|
|
|
2023-03-21 11:15:42 +00:00
|
|
|
|
StatusFlowSelector {
|
2022-09-09 22:09:37 +00:00
|
|
|
|
id: permissionsSelector
|
|
|
|
|
|
2022-06-09 15:27:14 +00:00
|
|
|
|
Layout.fillWidth: true
|
2023-03-21 11:15:42 +00:00
|
|
|
|
|
2022-06-09 15:27:14 +00:00
|
|
|
|
title: qsTr("Is allowed to")
|
2023-03-13 10:02:41 +00:00
|
|
|
|
placeholderText: qsTr("Example: View and post")
|
2023-03-21 11:15:42 +00:00
|
|
|
|
icon: Style.svg("profile/security")
|
2022-09-09 22:09:37 +00:00
|
|
|
|
|
2023-03-21 11:15:42 +00:00
|
|
|
|
readonly property bool empty:
|
|
|
|
|
d.dirtyValues.permissionType === PermissionTypes.Type.None
|
2023-02-11 19:03:57 +00:00
|
|
|
|
|
2023-03-21 11:15:42 +00:00
|
|
|
|
placeholderItem.visible: empty
|
|
|
|
|
addButton.visible: empty
|
2023-02-11 19:03:57 +00:00
|
|
|
|
|
2023-03-21 11:15:42 +00:00
|
|
|
|
StatusListItemTag {
|
|
|
|
|
readonly property int key: d.dirtyValues.permissionType
|
2023-02-11 19:03:57 +00:00
|
|
|
|
|
2023-03-21 11:15:42 +00:00
|
|
|
|
title: PermissionTypes.getName(key)
|
|
|
|
|
visible: !permissionsSelector.empty
|
|
|
|
|
|
|
|
|
|
asset.name: PermissionTypes.getIcon(key)
|
|
|
|
|
asset.bgColor: "transparent"
|
|
|
|
|
closeButtonVisible: false
|
|
|
|
|
titleText.font.pixelSize: Theme.primaryTextFontSize
|
|
|
|
|
leftPadding: 6
|
|
|
|
|
|
2023-03-02 15:58:26 +00:00
|
|
|
|
Binding on bgColor {
|
2024-03-18 17:28:09 +00:00
|
|
|
|
when: root.permissionTypeLimitReached && !root.isEditState
|
2023-03-02 15:58:26 +00:00
|
|
|
|
value: Theme.palette.dangerColor3
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Binding on titleText.color {
|
2024-03-18 17:28:09 +00:00
|
|
|
|
when: root.permissionTypeLimitReached && !root.isEditState
|
2023-03-02 15:58:26 +00:00
|
|
|
|
value: Theme.palette.dangerColor1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Binding on asset.color {
|
2024-03-18 17:28:09 +00:00
|
|
|
|
when: root.permissionTypeLimitReached && !root.isEditState
|
2023-03-02 15:58:26 +00:00
|
|
|
|
value: Theme.palette.dangerColor1
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-21 11:15:42 +00:00
|
|
|
|
MouseArea {
|
|
|
|
|
anchors.fill: parent
|
|
|
|
|
cursorShape: Qt.PointingHandCursor
|
|
|
|
|
|
|
|
|
|
onClicked: {
|
|
|
|
|
permissionsDropdown.mode = PermissionsDropdown.Mode.Update
|
|
|
|
|
permissionsDropdown.parent = parent
|
|
|
|
|
permissionsDropdown.x = mouse.x + d.dropdownHorizontalOffset
|
|
|
|
|
permissionsDropdown.y = d.dropdownVerticalOffset
|
|
|
|
|
permissionsDropdown.open()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-09-09 22:09:37 +00:00
|
|
|
|
|
|
|
|
|
PermissionsDropdown {
|
|
|
|
|
id: permissionsDropdown
|
|
|
|
|
|
2024-02-06 09:31:36 +00:00
|
|
|
|
allowCommunityOptions: root.showChannelSelector
|
2023-02-11 19:03:57 +00:00
|
|
|
|
initialPermissionType: d.dirtyValues.permissionType
|
2023-06-14 16:00:41 +00:00
|
|
|
|
enableAdminPermission: root.communityDetails.owner
|
2022-09-09 22:09:37 +00:00
|
|
|
|
|
|
|
|
|
onDone: {
|
2023-02-11 19:03:57 +00:00
|
|
|
|
if (d.dirtyValues.permissionType === permissionType) {
|
2023-01-20 11:15:05 +00:00
|
|
|
|
permissionsDropdown.close()
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-11 19:03:57 +00:00
|
|
|
|
d.dirtyValues.permissionType = permissionType
|
2022-09-09 22:09:37 +00:00
|
|
|
|
permissionsDropdown.close()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addButton.onClicked: {
|
|
|
|
|
permissionsDropdown.mode = PermissionsDropdown.Mode.Add
|
|
|
|
|
permissionsDropdown.parent = permissionsSelector.addButton
|
2022-12-20 00:54:50 +00:00
|
|
|
|
permissionsDropdown.x = permissionsSelector.addButton.width
|
|
|
|
|
+ d.dropdownHorizontalOffset
|
2022-09-09 22:09:37 +00:00
|
|
|
|
permissionsDropdown.y = 0
|
|
|
|
|
permissionsDropdown.open()
|
|
|
|
|
}
|
2022-06-09 15:27:14 +00:00
|
|
|
|
}
|
2023-04-25 20:09:00 +00:00
|
|
|
|
|
2024-02-06 09:31:36 +00:00
|
|
|
|
SequenceColumnLayout.Separator { visible: root.showChannelSelector }
|
2023-04-25 20:09:00 +00:00
|
|
|
|
|
2022-06-09 15:27:14 +00:00
|
|
|
|
StatusItemSelector {
|
2022-12-20 00:54:50 +00:00
|
|
|
|
id: inSelector
|
|
|
|
|
|
2023-01-20 11:15:05 +00:00
|
|
|
|
readonly property bool editable: !d.isCommunityPermission
|
|
|
|
|
|
|
|
|
|
addButton.visible: editable
|
|
|
|
|
itemsClickable: editable
|
2024-02-06 09:31:36 +00:00
|
|
|
|
visible: root.showChannelSelector
|
2022-06-09 15:27:14 +00:00
|
|
|
|
Layout.fillWidth: true
|
2023-01-20 11:15:05 +00:00
|
|
|
|
icon: d.isCommunityPermission ? Style.svg("communities") : Style.svg("create-category")
|
2022-06-09 15:27:14 +00:00
|
|
|
|
title: qsTr("In")
|
2023-03-13 10:02:41 +00:00
|
|
|
|
placeholderText: qsTr("Example: `#general` channel")
|
2022-12-20 00:54:50 +00:00
|
|
|
|
|
|
|
|
|
useLetterIdenticons: !wholeCommunitySelected || !inDropdown.communityImage
|
|
|
|
|
|
2023-01-27 09:22:04 +00:00
|
|
|
|
tagLeftPadding: wholeCommunitySelected ? 2 : 6
|
|
|
|
|
asset.width: wholeCommunitySelected ? 28 : 20
|
|
|
|
|
asset.height: asset.width
|
|
|
|
|
|
2022-12-20 00:54:50 +00:00
|
|
|
|
property bool wholeCommunitySelected: false
|
|
|
|
|
|
2023-01-20 11:15:05 +00:00
|
|
|
|
function openInDropdown(parent, x, y) {
|
|
|
|
|
inDropdown.parent = parent
|
|
|
|
|
inDropdown.x = x
|
|
|
|
|
inDropdown.y = y
|
|
|
|
|
|
|
|
|
|
const selectedChannels = []
|
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
if (!inSelector.wholeCommunitySelected) {
|
|
|
|
|
const model = d.dirtyValues.selectedChannelsModel
|
|
|
|
|
const count = model.count
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < count; i++)
|
|
|
|
|
selectedChannels.push(model.get(i).key)
|
|
|
|
|
}
|
2023-01-20 11:15:05 +00:00
|
|
|
|
|
|
|
|
|
inDropdown.setSelectedChannels(selectedChannels)
|
|
|
|
|
inDropdown.open()
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-20 00:54:50 +00:00
|
|
|
|
ListModel {
|
|
|
|
|
id: inModelCommunity
|
|
|
|
|
|
|
|
|
|
Component.onCompleted: {
|
|
|
|
|
append({
|
2023-03-07 08:47:04 +00:00
|
|
|
|
imageSource: inDropdown.communityImage,
|
2023-03-27 11:51:58 +00:00
|
|
|
|
isIcon: false,
|
2023-03-07 08:47:04 +00:00
|
|
|
|
text: inDropdown.communityName,
|
2023-01-27 09:22:04 +00:00
|
|
|
|
operator: OperatorsUtils.Operators.None,
|
2023-03-07 08:47:04 +00:00
|
|
|
|
color: inDropdown.communityColor
|
2022-12-20 00:54:50 +00:00
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
fix(permissions): fix hang when all channel perm check return (#14259)
* fix(permissions): fix hang when all channel perm check return
Fixes #14234
The problem was that we updated **all** the models from **all** the channels of a community each time the channel requirement checks returned.
The fix is to first of all, make sure we don't call that check too often. It sometimes got called twice in a row by accident.
The other better fix is to check if anything actually changed before updating. This solves the issue almost entirely. Since the permissions almost never change, the updates now take only a second.
* fix(permisisons): never run permission checks for privileged users
Also fixes #14234 but for admins, TMs and Owners.
Admins+ were still getting the hang, because the permission checks always returned something different than the models, because the models knew that admins have access to everything, but the permission check was running as if it were a normal user (I think, un-tested).
Anyway, the solution is more simple, we never need to run the permission checks on admins+, because they always have access to everything!
* fix(Communities): prevent channels model from emitting unnecessary signals
Closes: #14274
* chore(Communities): improve channels metadata lookup performance
ChannelsSelectionModel is removed, replaced with plain LeftJoinModel.
Transformations of left-side model are done in a single place, not in
every delegate making the join.
* only call update functions when there is something to update + move permission model creation when needed
---------
Co-authored-by: Michał Cieślak <michalcieslak@status.im>
2024-04-04 15:26:44 +00:00
|
|
|
|
LeftJoinModel {
|
2023-02-28 12:44:46 +00:00
|
|
|
|
id: channelsSelectionModel
|
|
|
|
|
|
fix(permissions): fix hang when all channel perm check return (#14259)
* fix(permissions): fix hang when all channel perm check return
Fixes #14234
The problem was that we updated **all** the models from **all** the channels of a community each time the channel requirement checks returned.
The fix is to first of all, make sure we don't call that check too often. It sometimes got called twice in a row by accident.
The other better fix is to check if anything actually changed before updating. This solves the issue almost entirely. Since the permissions almost never change, the updates now take only a second.
* fix(permisisons): never run permission checks for privileged users
Also fixes #14234 but for admins, TMs and Owners.
Admins+ were still getting the hang, because the permission checks always returned something different than the models, because the models knew that admins have access to everything, but the permission check was running as if it were a normal user (I think, un-tested).
Anyway, the solution is more simple, we never need to run the permission checks on admins+, because they always have access to everything!
* fix(Communities): prevent channels model from emitting unnecessary signals
Closes: #14274
* chore(Communities): improve channels metadata lookup performance
ChannelsSelectionModel is removed, replaced with plain LeftJoinModel.
Transformations of left-side model are done in a single place, not in
every delegate making the join.
* only call update functions when there is something to update + move permission model creation when needed
---------
Co-authored-by: Michał Cieślak <michalcieslak@status.im>
2024-04-04 15:26:44 +00:00
|
|
|
|
leftModel: d.dirtyValues.selectedChannelsModel
|
|
|
|
|
rightModel: root.channelsModel
|
|
|
|
|
joinRole: "key"
|
2023-02-28 12:44:46 +00:00
|
|
|
|
}
|
|
|
|
|
|
2022-12-20 00:54:50 +00:00
|
|
|
|
InDropdown {
|
|
|
|
|
id: inDropdown
|
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
model: root.channelsModel
|
2022-12-20 00:54:50 +00:00
|
|
|
|
|
2023-03-07 08:47:04 +00:00
|
|
|
|
communityName: root.communityDetails.name
|
|
|
|
|
communityImage: root.communityDetails.image
|
|
|
|
|
communityColor: root.communityDetails.color
|
2022-12-20 00:54:50 +00:00
|
|
|
|
|
|
|
|
|
onChannelsSelected: {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedChannelsModel.clear()
|
2023-03-13 10:02:41 +00:00
|
|
|
|
inSelector.model = 0
|
2022-12-20 00:54:50 +00:00
|
|
|
|
inSelector.wholeCommunitySelected = false
|
|
|
|
|
|
2023-02-28 14:10:50 +00:00
|
|
|
|
const modelData = channels.map(key => ({ key }))
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedChannelsModel.append(modelData)
|
2022-12-20 00:54:50 +00:00
|
|
|
|
|
2023-03-13 10:02:41 +00:00
|
|
|
|
inSelector.model = channelsSelectionModel
|
2022-12-20 00:54:50 +00:00
|
|
|
|
close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onCommunitySelected: {
|
2023-03-07 08:47:04 +00:00
|
|
|
|
d.dirtyValues.selectedChannelsModel.clear()
|
2022-12-20 00:54:50 +00:00
|
|
|
|
inSelector.wholeCommunitySelected = true
|
2023-03-13 10:02:41 +00:00
|
|
|
|
inSelector.model = inModelCommunity
|
2022-12-20 00:54:50 +00:00
|
|
|
|
close()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
addButton.onClicked: {
|
|
|
|
|
inDropdown.acceptMode = InDropdown.AcceptMode.Add
|
|
|
|
|
openInDropdown(inSelector.addButton,
|
|
|
|
|
inSelector.addButton.width + d.dropdownHorizontalOffset, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onItemClicked: {
|
|
|
|
|
if (mouse.button !== Qt.LeftButton)
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
inDropdown.acceptMode = InDropdown.AcceptMode.Update
|
|
|
|
|
openInDropdown(item, mouse.x + d.dropdownHorizontalOffset,
|
|
|
|
|
d.dropdownVerticalOffset)
|
|
|
|
|
}
|
2022-06-09 15:27:14 +00:00
|
|
|
|
}
|
|
|
|
|
Separator {
|
|
|
|
|
Layout.topMargin: 24
|
2023-07-25 15:25:41 +00:00
|
|
|
|
color: Theme.palette.baseColor2
|
2022-06-09 15:27:14 +00:00
|
|
|
|
}
|
2023-02-08 17:13:25 +00:00
|
|
|
|
|
2024-02-06 09:31:36 +00:00
|
|
|
|
StatusIconSwitch {
|
2022-06-09 15:27:14 +00:00
|
|
|
|
Layout.topMargin: 12
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Layout.leftMargin: 16
|
|
|
|
|
Layout.rightMargin: Layout.leftMargin
|
2023-02-08 17:13:25 +00:00
|
|
|
|
|
2023-02-11 19:03:57 +00:00
|
|
|
|
enabled: d.dirtyValues.permissionType !== PermissionTypes.Type.Admin
|
2023-02-08 17:13:25 +00:00
|
|
|
|
checked: d.dirtyValues.isPrivate
|
2024-02-06 09:31:36 +00:00
|
|
|
|
title: qsTr("Hide permission")
|
|
|
|
|
subTitle: qsTr("Make this permission hidden from members who don’t meet its requirements")
|
|
|
|
|
icon: "hide"
|
2023-02-08 17:13:25 +00:00
|
|
|
|
onToggled: d.dirtyValues.isPrivate = checked
|
2022-06-09 15:27:14 +00:00
|
|
|
|
}
|
2023-01-17 12:22:28 +00:00
|
|
|
|
|
2023-04-27 22:13:59 +00:00
|
|
|
|
WarningPanel {
|
2023-02-14 20:39:18 +00:00
|
|
|
|
id: duplicationPanel
|
|
|
|
|
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Layout.topMargin: 50 // by desing
|
2023-02-14 23:53:01 +00:00
|
|
|
|
|
2023-03-02 15:58:26 +00:00
|
|
|
|
text: {
|
|
|
|
|
if (root.permissionTypeLimitReached)
|
|
|
|
|
return PermissionTypes.getPermissionsLimitWarning(
|
|
|
|
|
d.dirtyValues.permissionType)
|
|
|
|
|
|
|
|
|
|
if (root.permissionDuplicated)
|
|
|
|
|
return qsTr("Permission with same properties is already active, edit properties to create a new permission.")
|
|
|
|
|
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
|
2024-03-18 17:28:09 +00:00
|
|
|
|
visible: root.permissionDuplicated || (root.permissionTypeLimitReached && !root.isEditState)
|
2023-02-14 20:39:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-08-08 16:17:24 +00:00
|
|
|
|
StatusWarningBox {
|
|
|
|
|
Layout.fillWidth: true
|
|
|
|
|
Layout.topMargin: Style.current.padding
|
2024-02-06 09:31:36 +00:00
|
|
|
|
visible: root.showChannelSelector
|
2023-08-08 16:17:24 +00:00
|
|
|
|
icon: "desktop"
|
|
|
|
|
text: qsTr("Any changes to community permissions will take effect after the control node receives and processes them")
|
|
|
|
|
borderColor: Theme.palette.baseColor1
|
|
|
|
|
iconColor: textColor
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 15:27:14 +00:00
|
|
|
|
StatusButton {
|
|
|
|
|
Layout.preferredHeight: 44
|
|
|
|
|
Layout.alignment: Qt.AlignHCenter
|
|
|
|
|
Layout.fillWidth: true
|
2023-03-07 08:47:04 +00:00
|
|
|
|
Layout.topMargin: Style.current.bigPadding
|
2023-02-14 23:53:01 +00:00
|
|
|
|
|
2024-02-06 09:31:36 +00:00
|
|
|
|
visible: !root.isEditState && root.showChannelSelector
|
2023-02-14 23:53:01 +00:00
|
|
|
|
text: qsTr("Create permission")
|
2024-02-07 10:30:46 +00:00
|
|
|
|
enabled: root.saveEnabled
|
2023-02-08 23:22:57 +00:00
|
|
|
|
|
|
|
|
|
onClicked: root.createPermissionClicked()
|
2022-06-09 15:27:14 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|