diff --git a/ui/app/AppLayouts/Chat/controls/community/EnsPanel.qml b/ui/app/AppLayouts/Chat/controls/community/EnsPanel.qml index 6e61d0157d..039bb0f0b0 100644 --- a/ui/app/AppLayouts/Chat/controls/community/EnsPanel.qml +++ b/ui/app/AppLayouts/Chat/controls/community/EnsPanel.qml @@ -53,6 +53,7 @@ ColumnLayout { // intercepting event by the StatusRadioButton MouseArea { anchors.fill: parent + cursorShape: Qt.PointingHandCursor onClicked: root.ensType = EnsPanel.EnsType.Any } } @@ -76,6 +77,7 @@ ColumnLayout { MouseArea { anchors.fill: parent + cursorShape: Qt.PointingHandCursor onClicked: root.ensType = EnsPanel.EnsType.CustomSubdomain } } diff --git a/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml b/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml index 680e026a07..53d06a9d2f 100644 --- a/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml +++ b/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml @@ -53,6 +53,9 @@ StatusDropdown { width: d.defaultWidth padding: d.padding + // force keeping within the bounds of the enclosing window + margins: 0 + onClosed: root.reset() enum FlowType { diff --git a/ui/app/AppLayouts/Chat/controls/community/PermissionListItem.qml b/ui/app/AppLayouts/Chat/controls/community/PermissionListItem.qml new file mode 100644 index 0000000000..523bc3e29a --- /dev/null +++ b/ui/app/AppLayouts/Chat/controls/community/PermissionListItem.qml @@ -0,0 +1,50 @@ +import StatusQ.Core 0.1 +import StatusQ.Components 0.1 +import StatusQ.Controls 0.1 + +import QtQuick 2.14 +import QtQuick.Controls 2.14 + +StatusListItem { + id: root + + property ButtonGroup buttonGroup + property alias checked: radioButton.checked + readonly property alias radioButton: radioButton + + implicitHeight: 44 + leftPadding: 8 + rightPadding: 9 + statusListItemTitle.font.pixelSize: 13 + + statusListItemTitleArea.anchors.leftMargin: 8 + + asset.bgWidth: 32 + asset.bgHeight: 32 + + components: [ + StatusRadioButton { + id: radioButton + + // reference to root for better integration with ButtonGroup + // by accessing main component via ButtonGroup::checkedButton.item + readonly property alias item: root + + size: StatusRadioButton.Size.Small + ButtonGroup.group: root.buttonGroup + + rightPadding: 0 + } + ] + + // using MouseArea instead of build-in 'clicked' signal to avoid + // intercepting event by the StatusRadioButton + MouseArea { + anchors.fill: parent + onClicked: { + if (!radioButton.checked) + radioButton.toggle() + } + cursorShape: Qt.PointingHandCursor + } +} diff --git a/ui/app/AppLayouts/Chat/controls/community/PermissionTypes.qml b/ui/app/AppLayouts/Chat/controls/community/PermissionTypes.qml new file mode 100644 index 0000000000..eb9b929392 --- /dev/null +++ b/ui/app/AppLayouts/Chat/controls/community/PermissionTypes.qml @@ -0,0 +1,7 @@ +import QtQml 2.14 + +QtObject { + enum Type { + None, Admin, Member, Moderator, ViewAndPost, Read + } +} diff --git a/ui/app/AppLayouts/Chat/controls/community/PermissionsDropdown.qml b/ui/app/AppLayouts/Chat/controls/community/PermissionsDropdown.qml new file mode 100644 index 0000000000..79ff813f10 --- /dev/null +++ b/ui/app/AppLayouts/Chat/controls/community/PermissionsDropdown.qml @@ -0,0 +1,202 @@ +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.Controls 0.1 + +import shared.panels 1.0 + + +StatusDropdown { + id: root + + property int mode: PermissionsDropdown.Mode.Add + property int initialPermissionType: PermissionTypes.Type.None + + enum Mode { + Add, Update + } + + signal done(int permissionType, string title, string asset) + + width: d.width + padding: d.padding + + // force keeping within the bounds of the enclosing window + margins: 0 + + onAboutToShow: { + group.checkState = Qt.Unchecked + d.initialPermissionTypeChanged() + } + + QtObject { + id: d + + // internals + readonly property int initialPermissionType: root.initialPermissionType + + // values from design + readonly property int padding: 8 + readonly property int width: 289 + readonly property int sectionHeight: 34 + readonly property int sectionFontSize: 12 + readonly property int extraMarginForText: 8 + + readonly property int separatorTopMargin: 4 + readonly property int separatorBottomMargin: 12 + + readonly property int descriptionFontSize: 13 + readonly property int descriptionLineHeight: 18 + + readonly property int buttonTopMargin: 32 + } + + ButtonGroup { + id: group + } + + contentItem: ColumnLayout { + spacing: 0 + + Item { + 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 + } + } + + 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 = `${warning}` + return `${generalInfo}

${warningStyled} ${warningExplanation}` + } + + title: qsTr("Become admin") + asset.name: "admin" + checked: d.initialPermissionType === permissionType + buttonGroup: group + + 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 + + Layout.fillWidth: true + } + + Item { + 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 + } + } + + 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 + + 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 + + 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("Read") + asset.name: "show" + checked: d.initialPermissionType === permissionType + buttonGroup: group + + Layout.fillWidth: true + } + + Separator { + visible: !!group.checkedButton + + Layout.fillWidth: true + Layout.topMargin: d.separatorTopMargin + Layout.bottomMargin: d.separatorBottomMargin + } + + StatusBaseText { + Layout.fillWidth: true + Layout.leftMargin: d.extraMarginForText + + visible: !!group.checkedButton + text: group.checkedButton ? group.checkedButton.item.description : "" + + textFormat: Text.StyledText + wrapMode: Text.Wrap + color: Theme.palette.baseColor1 + font.pixelSize: d.descriptionFontSize + lineHeight: d.descriptionLineHeight + lineHeightMode: Text.FixedHeight + } + + StatusButton { + Layout.fillWidth: true + Layout.topMargin: d.buttonTopMargin + + 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) + } + } +} diff --git a/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml b/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml index 9635253bdd..519a4ca005 100644 --- a/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml +++ b/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml @@ -32,6 +32,7 @@ Flickable { id: d property bool isPrivate: false property ListModel permissions: ListModel{} + property int permissionType: PermissionTypes.Type.None } contentWidth: mainLayout.width @@ -116,7 +117,7 @@ Flickable { onAddEns: { const key = any ? "EnsAny" : "EnsCustom" const name = any ? "" : customDomain - const icon = "qrc:imports/assets/icons/profile/ensUsernames.svg" + const icon = Style.svg("ensUsernames") holdingsModel.append({type: HoldingTypes.Type.Ens, key, name, amount: 1, imageSource: icon, operator }) d.permissions.append([{ key }, { operator }]) @@ -144,7 +145,7 @@ Flickable { onUpdateEns: { const key = any ? "EnsAny" : "EnsCustom" const name = any ? "" : customDomain - const icon = "qrc:imports/assets/icons/profile/ensUsernames.svg" + const icon = Style.svg("ensUsernames") holdingsModel.set(tokensSelector.editedIndex, { type: HoldingTypes.Type.Ens, key, name: name, amount: 1, imageSource: icon }) dropdown.close() @@ -166,7 +167,7 @@ Flickable { dropdown.x = tokensSelector.addButton.width + 4 dropdown.y = 0 - if (tokensSelector.itemsModel.count === 0) + if (holdingsModel.count === 0) dropdown.openFlow(HoldingsDropdown.FlowType.Add) else dropdown.openFlow(HoldingsDropdown.FlowType.AddWithOperators) @@ -214,11 +215,59 @@ Flickable { 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") + + Binding on itemsModel { + when: d.permissionType !== PermissionTypes.Type.None + value: QtObject { + id: permissionsListObjectModel + + readonly property int operator: SQ.Utils.Operators.None + property string text: "" + property string imageSource: "" + } + } + + addButton.visible: d.permissionType === PermissionTypes.Type.None + + PermissionsDropdown { + id: permissionsDropdown + + initialPermissionType: d.permissionType + + onDone: { + d.permissionType = permissionType + permissionsListObjectModel.text = title + permissionsListObjectModel.imageSource = asset + permissionsDropdown.close() + } + } + + addButton.onClicked: { + permissionsDropdown.mode = PermissionsDropdown.Mode.Add + permissionsDropdown.parent = permissionsSelector.addButton + permissionsDropdown.x = permissionsSelector.addButton.width + 4 + permissionsDropdown.y = 0 + permissionsDropdown.open() + } + + onItemClicked: { + if (mouse.button !== Qt.LeftButton) + return + + permissionsDropdown.mode = PermissionsDropdown.Mode.Update + permissionsDropdown.parent = item + permissionsDropdown.x = mouse.x + 4 + permissionsDropdown.y = 1 + permissionsDropdown.open() + } } Rectangle { Layout.leftMargin: 16