status-desktop/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml

549 lines
20 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Utils 0.1
import AppLayouts.Chat.helpers 1.0
import utils 1.0
import shared.panels 1.0
import SortFilterProxyModel 0.2
import AppLayouts.Chat.panels.communities 1.0
import "../../../Chat/controls/community"
StatusScrollView {
id: root
property var rootStore
property var store
property int viewWidth: 560 // by design
property bool isEditState: false
property bool dirty: {
let trick = d.triggerDirtyTool // Trick: Used to force the reevaluation of dirty when an item of the list is updated
// Holdings:
const dirtyHoldingsList = d.checkIfHoldingsDirty()
// 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
}
// TODO: Channels:
let dirtyChannelsList = false
return dirtyHoldingsList || dirtyPermissionObj || dirtyChannelsList || d.dirtyValues.isPrivateDirty
}
property bool saveChanges: false
property bool resetChanges: false
property int permissionIndex
// roles: type, key, name, amount, imageSource
property var holdingsModel: ListModel {}
// roles: key, text, imageSource
property var permissionObject
// TODO roles:
property var channelsModel: ListModel {}
property bool isPrivate
signal permissionCreated()
QtObject {
id: d
readonly property int maxHoldingsItems: 5
readonly property int dropdownHorizontalOffset: 4
readonly property int dropdownVerticalOffset: 1
property int permissionType: PermissionTypes.Type.None
property bool triggerDirtyTool: false // Trick: Used to force the reevaluation of dirty when an item of the list is updated
property QtObject dirtyValues: QtObject {
property ListModel holdingsModel: ListModel {}
property QtObject permissionObject: QtObject {
property var key: null
property string text: ""
property string imageSource: ""
}
property bool isPrivateDirty: false
// TODO: Channels
}
function saveChanges() {
root.store.editPermission(root.permissionIndex,
d.dirtyValues.holdingsModel,
d.dirtyValues.permissionObject,
root.channelsModel,
d.dirtyValues.isPrivateDirty ? !root.isPrivate : root.isPrivate)
}
function loadInitValues() {
// Holdings:
d.dirtyValues.holdingsModel.clear()
if(root.holdingsModel) {
for(let i = 0; i < root.holdingsModel.count; i++) {
let item = root.holdingsModel.get(i)
let initItem = null
if(item.shortName) {
initItem = {
type: item.type,
key: item.key,
name: item.name,
shortName: item.shortName,
amount: item.amount,
imageSource: item.imageSource
}
}
else {
initItem = {
type: item.type,
key: item.key,
name: item.name,
amount: item.amount,
imageSource: item.imageSource
}
}
d.dirtyValues.holdingsModel.append(initItem)
}
}
// 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 : ""
// TODO: Channels
// Is private permission
d.dirtyValues.isPrivateDirty = false
}
function checkIfHoldingsDirty() {
let dirty = false
if(root.holdingsModel) {
if(root.holdingsModel.count !== d.dirtyValues.holdingsModel.count) {
dirty = true
}
else {
// Check element by element
let equals = 0
for(let i = 0; i < root.holdingsModel.count; i++) {
const item1 = root.holdingsModel.get(i)
for(let j = 0; j < d.dirtyValues.holdingsModel.count; j++) {
let item2 = d.dirtyValues.holdingsModel.get(j)
// key, name, shortName, amount
if((item1.key === item2.key) &&
(item1.name === item2.name) &&
(item1.shortName === item2.shortName) &&
(item1.amount === item2.amount)) {
equals = equals + 1
}
}
}
dirty = (equals !== root.holdingsModel.count)
}
}
else {
dirty = (d.dirtyValues.holdingsModel.count !== 0)
}
return dirty
}
function holdingsTextFormat(type, name, amount) {
return CommunityPermissionsHelpers.setHoldingsTextFormat(type, name, amount)
}
}
contentWidth: mainLayout.width
contentHeight: mainLayout.height
onSaveChangesChanged: if(saveChanges) d.saveChanges()
onResetChangesChanged: if(resetChanges) d.loadInitValues()
onPermissionObjectChanged: d.loadInitValues()
ColumnLayout {
id: mainLayout
width: root.viewWidth
spacing: 0
CurveSeparatorWithText {
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 14
text: qsTr("Anyone")
}
StatusItemSelector {
id: tokensSelector
property int editedIndex
Layout.fillWidth: true
icon: Style.svg("contact_verified")
title: qsTr("Who holds")
defaultItemText: qsTr("Example: 10 SNT")
tagLeftPadding: 2
asset.height: 28
asset.width: asset.height
addButton.visible: itemsModel.count < d.maxHoldingsItems
itemsModel: SortFilterProxyModel {
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)
}
}
HoldingsDropdown {
id: dropdown
store: root.store
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 })
}
onAddAsset: {
const modelItem = CommunityPermissionsHelpers.getTokenByKey(store.assetsModel, key)
addItem(HoldingTypes.Type.Asset, modelItem, amount)
dropdown.close()
}
onAddCollectible: {
const modelItem = CommunityPermissionsHelpers.getTokenByKey(store.collectiblesModel, key)
addItem(HoldingTypes.Type.Collectible, modelItem, amount)
dropdown.close()
}
onAddEns: {
const key = "ENS"
const icon = Style.svg("profile/ensUsernames")
d.dirtyValues.holdingsModel.append({type: HoldingTypes.Type.Ens, key, name: domain, amount: 1, imageSource: icon })
dropdown.close()
}
onUpdateAsset: {
const modelItem = CommunityPermissionsHelpers.getTokenByKey(store.assetsModel, key)
const name = modelItem.shortName ? modelItem.shortName : modelItem.name
const imageSource = modelItem.iconSource.toString()
d.dirtyValues.holdingsModel.set(tokensSelector.editedIndex, { type: HoldingTypes.Type.Asset, key, name, amount, imageSource })
d.triggerDirtyTool = !d.triggerDirtyTool
dropdown.close()
}
onUpdateCollectible: {
const modelItem = CommunityPermissionsHelpers.getTokenByKey(store.collectiblesModel, key)
const name = modelItem.name
const imageSource = modelItem.iconSource.toString()
d.dirtyValues.holdingsModel.set(tokensSelector.editedIndex, { type: HoldingTypes.Type.Collectible, key, name, amount, imageSource })
d.triggerDirtyTool = !d.triggerDirtyTool
dropdown.close()
}
onUpdateEns: {
const key = "ENS"
const icon = Style.svg("profile/ensUsernames")
d.dirtyValues.holdingsModel.set(tokensSelector.editedIndex, { type: HoldingTypes.Type.Ens, key, name: domain, amount: 1, imageSource: icon })
d.triggerDirtyTool = !d.triggerDirtyTool
dropdown.close()
}
onRemoveClicked: {
d.dirtyValues.holdingsModel.remove(tokensSelector.editedIndex)
dropdown.close()
}
}
addButton.onClicked: {
dropdown.parent = tokensSelector.addButton
dropdown.x = tokensSelector.addButton.width + d.dropdownHorizontalOffset
dropdown.y = 0
dropdown.open()
}
onItemClicked: {
if (mouse.button !== Qt.LeftButton)
return
dropdown.parent = item
dropdown.x = mouse.x + d.dropdownHorizontalOffset
dropdown.y = d.dropdownVerticalOffset
const modelItem = tokensSelector.itemsModel.get(index)
switch(modelItem.type) {
case HoldingTypes.Type.Asset:
dropdown.assetKey = modelItem.key
dropdown.assetAmount = modelItem.amount
break
case HoldingTypes.Type.Collectible:
dropdown.collectibleKey = modelItem.key
dropdown.collectibleAmount = modelItem.amount
break
case HoldingTypes.Type.Ens:
dropdown.ensDomainName = modelItem.name
break
default:
console.warn("Unsupported holdings type.")
}
dropdown.setActiveTab(modelItem.type)
dropdown.openUpdateFlow()
editedIndex = index
}
}
Rectangle {
Layout.leftMargin: 16
Layout.preferredWidth: 2
Layout.preferredHeight: 24
color: Style.current.separator
}
StatusItemSelector {
id: permissionsSelector
Layout.fillWidth: true
icon: Style.svg("profile/security")
iconSize: 24
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
PermissionsDropdown {
id: permissionsDropdown
initialPermissionType: d.permissionType
onDone: {
d.permissionType = permissionType
d.dirtyValues.permissionObject.key = permissionType
d.dirtyValues.permissionObject.text = title
d.dirtyValues.permissionObject.imageSource = asset
permissionsDropdown.close()
}
}
addButton.onClicked: {
permissionsDropdown.mode = PermissionsDropdown.Mode.Add
permissionsDropdown.parent = permissionsSelector.addButton
permissionsDropdown.x = permissionsSelector.addButton.width
+ d.dropdownHorizontalOffset
permissionsDropdown.y = 0
permissionsDropdown.open()
}
onItemClicked: {
if (mouse.button !== Qt.LeftButton)
return
permissionsDropdown.mode = PermissionsDropdown.Mode.Update
permissionsDropdown.parent = item
permissionsDropdown.x = mouse.x + d.dropdownHorizontalOffset
permissionsDropdown.y = d.dropdownVerticalOffset
permissionsDropdown.open()
}
}
Rectangle {
Layout.leftMargin: 16
Layout.preferredWidth: 2
Layout.preferredHeight: 24
color: Style.current.separator
}
StatusItemSelector {
id: inSelector
Layout.fillWidth: true
icon: Style.svg("create-category")
iconSize: 24
title: qsTr("In")
defaultItemText: qsTr("Example: `#general` channel")
useLetterIdenticons: !wholeCommunitySelected || !inDropdown.communityImage
property bool wholeCommunitySelected: false
ListModel {
id: inModelCommunity
readonly property string colorWorkaround: inDropdown.communityData.color
Component.onCompleted: {
append({
imageSource: inDropdown.communityData.image,
text: inDropdown.communityData.name,
operator: OperatorsUtils.Operators.None
})
setProperty(0, "color", colorWorkaround)
}
}
ListModel {
id: inModelChannels
}
InDropdown {
id: inDropdown
model: root.rootStore.chatCommunitySectionModule.model
readonly property var communityData: rootStore.mainModuleInst.activeSection
communityName: communityData.name
communityImage: communityData.image
communityColor: communityData.color
onChannelsSelected: {
inModelChannels.clear()
inSelector.itemsModel = 0
inSelector.wholeCommunitySelected = false
channels.map(channel => {
inModelChannels.append({
itemId: channel.itemId,
text: "#" + channel.name,
emoji: channel.emoji,
color: channel.color,
operator: OperatorsUtils.Operators.None
})
})
inSelector.itemsModel = inModelChannels
close()
}
onCommunitySelected: {
inModelChannels.clear()
inSelector.wholeCommunitySelected = true
inSelector.itemsModel = inModelCommunity
close()
}
}
function openInDropdown(parent, x, y) {
inDropdown.parent = parent
inDropdown.x = x
inDropdown.y = y
const selectedChannels = []
if (!inSelector.wholeCommunitySelected)
for (let i = 0; i < inModelChannels.count; i++)
selectedChannels.push(inModelChannels.get(i).itemId)
inDropdown.setSelectedChannels(selectedChannels)
inDropdown.open()
}
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)
}
}
Separator {
Layout.topMargin: 24
}
RowLayout {
Layout.topMargin: 12
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: Layout.leftMargin
spacing: 16
StatusRoundIcon {
asset.name: "hide"
}
ColumnLayout {
Layout.fillWidth: true
StatusBaseText {
text: qsTr("Private")
color: Theme.palette.directColor1
font.pixelSize: 15
}
StatusBaseText {
Layout.fillWidth: true
Layout.fillHeight: true
text: qsTr("Make this permission private to hide it from members who dont meet its requirements")
color: Theme.palette.baseColor1
font.pixelSize: 15
lineHeight: 1.2
wrapMode: Text.WordWrap
elide: Text.ElideRight
clip: true
}
}
StatusSwitch {
checked: d.dirtyValues.isPrivateDirty ? !root.isPrivate : root.isPrivate
onToggled: d.dirtyValues.isPrivateDirty = (root.isPrivate !== checked)
}
}
PermissionConflictWarningPanel{
id: conflictPanel
visible: store.permissionConflict.exists
Layout.fillWidth: true
Layout.topMargin: 50 // by desing
holdings: store.permissionConflict.holdings
permissions: store.permissionConflict.permissions
channels: store.permissionConflict.channels
}
StatusButton {
visible: !root.isEditState
Layout.topMargin: conflictPanel.visible ? conflictPanel.Layout.topMargin : 24 // by design
text: qsTr("Create permission")
enabled: d.dirtyValues.holdingsModel && d.dirtyValues.holdingsModel.count > 0 && d.dirtyValues.permissionObject.key !== null
Layout.preferredHeight: 44
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
onClicked: {
root.store.createPermission(d.dirtyValues.holdingsModel,
d.dirtyValues.permissionObject,
d.dirtyValues.isPrivateDirty ? !root.isPrivate : root.isPrivate,
root.channelsModel)
root.permissionCreated()
}
}
}
}