diff --git a/src/app/global/local_account_sensitive_settings.nim b/src/app/global/local_account_sensitive_settings.nim index 6fa33a5d8e..4da0242ef8 100644 --- a/src/app/global/local_account_sensitive_settings.nim +++ b/src/app/global/local_account_sensitive_settings.nim @@ -8,6 +8,8 @@ const LSS_KEY_WALLET_SPLIT_VIEW* = "walletSplitView" const LSS_KEY_PROFILE_SPLIT_VIEW* = "profileSplitView" const LSS_KEY_IS_WALLET_ENABLED* = "isExperimentalWalletEnabled" const DEFAULT_IS_WALLET_ENABLED = false +const LSS_KEY_IS_COMMUNITY_PERMISSIONS_ENABLED* = "isExperimentalCommunityPermissionsEnabled" +const DEFAULT_IS_COMMUNITY_PERMISSIONS_ENABLED = false const LSS_KEY_NODE_MANAGEMENT_ENABLED* = "nodeManagementEnabled" const DEFAULT_NODE_MANAGEMENT_ENABLED = false const LSS_KEY_IS_BROWSER_ENABLED* = "isExperimentalBrowserEnabled" @@ -191,6 +193,18 @@ QtObject: write = setProfileSplitView notify = profileSplitViewChanged + proc isCommunityPermissionsEnabledChanged*(self: LocalAccountSensitiveSettings) {.signal.} + proc getIsCommunityPermissionsEnabled*(self: LocalAccountSensitiveSettings): bool {.slot.} = + getSettingsProp[bool](self, LSS_KEY_IS_COMMUNITY_PERMISSIONS_ENABLED, newQVariant(DEFAULT_IS_COMMUNITY_PERMISSIONS_ENABLED)) + proc setIsCommunityPermissionsEnabled*(self: LocalAccountSensitiveSettings, value: bool) {.slot.} = + setSettingsProp(self, LSS_KEY_IS_COMMUNITY_PERMISSIONS_ENABLED, newQVariant(value)): + self.isCommunityPermissionsEnabledChanged() + + QtProperty[bool] isCommunityPermissionsEnabled: + read = getIsCommunityPermissionsEnabled + write = setIsCommunityPermissionsEnabled + notify = isCommunityPermissionsEnabledChanged + proc isWalletEnabledChanged*(self: LocalAccountSensitiveSettings) {.signal.} proc getIsWalletEnabled*(self: LocalAccountSensitiveSettings): bool {.slot.} = getSettingsProp[bool](self, LSS_KEY_IS_WALLET_ENABLED, newQVariant(DEFAULT_IS_WALLET_ENABLED)) @@ -740,6 +754,7 @@ QtObject: of LSS_KEY_WALLET_SPLIT_VIEW: self.walletSplitViewChanged() of LSS_KEY_PROFILE_SPLIT_VIEW: self.profileSplitViewChanged() of LSS_KEY_IS_WALLET_ENABLED: self.isWalletEnabledChanged() + of LSS_KEY_IS_COMMUNITY_PERMISSIONS_ENABLED: self.isCommunityPermissionsEnabledChanged() of LSS_KEY_NODE_MANAGEMENT_ENABLED: self.nodeManagementEnabledChanged() of LSS_KEY_IS_BROWSER_ENABLED: self.isBrowserEnabledChanged() of LSS_KEY_SHOW_ONLINE_USERS: self.showOnlineUsersChanged() diff --git a/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml b/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml new file mode 100644 index 0000000000..d253148596 --- /dev/null +++ b/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml @@ -0,0 +1,277 @@ +import QtQuick 2.13 +import QtQuick.Layouts 1.14 +import QtQuick.Controls 2.13 +import QtGraphicalEffects 1.13 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Components 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Controls.Validators 0.1 +import StatusQ.Core.Utils 0.1 as SQ + +import utils 1.0 + +StatusDropdown { + id: root + + property real tokenAmountValue: 0 + property string tokenName: d.defaultTokenNameText + property url tokenImage: "" + property int operator: SQ.Utils.Operators.None + property bool withOperatorSelector: true + + signal addToken(string tokenText, url tokenImage, int operator) + + function reset() { + root.tokenAmountValue = 0 + root.tokenName = d.defaultTokenNameText + root.tokenImage = "" + root.operator = SQ.Utils.Operators.None + } + + QtObject { + id: d + + readonly property bool ready: root.tokenAmountValue > 0 && root.tokenName !== d.defaultTokenNameText + + // By design values: + readonly property int initialHeight: 232 + readonly property int mainHeight: 256 + readonly property int operatorsHeight: 96 + readonly property int extendedHeight: 417 + readonly property int defaultWidth: 289 + readonly property int operatorsWidth: 159 + + property string defaultTokenNameText: qsTr("Choose token") + + function selectInitState() { + if(root.withOperatorSelector) + loader.sourceComponent = operatorsSelectorView + else + loader.sourceComponent = tabsView + } + } + + width: d.defaultWidth + height: d.initialHeight + + contentItem: Loader { + id: loader + anchors.fill: parent + sourceComponent: root.withOperatorSelector ? operatorsSelectorView : tabsView + + onSourceComponentChanged: { + if(sourceComponent == tokensExtendedView) { + root.height = Math.min(item.contentHeight + item.anchors.topMargin + item.anchors.bottomMargin, d.extendedHeight) + root.width = d.defaultWidth + } + else if(sourceComponent == operatorsSelectorView) { + root.height = d.operatorsHeight + root.width = d.operatorsWidth + } + else if(sourceComponent == tabsView && root.withOperatorSelector) { + root.height = d.mainHeight + root.width = d.defaultWidth + } + else if(sourceComponent == tabsView && !root.withOperatorSelector) { + root.height = d.initialHeight + root.width = d.defaultWidth + } + } + } + + onOpened: d.selectInitState() + onClosed: root.reset() + onWithOperatorSelectorChanged: d.selectInitState() + + Component { + id: tabsView + + ColumnLayout { + anchors.fill: parent + anchors.margins: 8 + anchors.topMargin: 16 + spacing: 8 + StatusIconTextButton { + visible: root.withOperatorSelector + Layout.leftMargin: 8 + spacing: 0 + statusIcon: "next" + icon.width: 12 + icon.height: 12 + iconRotation: 180 + text: qsTr("Back") + onClicked: loader.sourceComponent = operatorsSelectorView + } + StatusSwitchTabBar { + id: tabBar + Layout.preferredWidth: 273 // by design + Layout.preferredHeight: 36 // by design + StatusSwitchTabButton { + text: qsTr("Token") + fontPixelSize: 13 + } + StatusSwitchTabButton { + text: qsTr("Collectibles") + fontPixelSize: 13 + enabled: false // TODO + } + StatusSwitchTabButton { + text: qsTr("ENS") + fontPixelSize: 13 + enabled: false // TODO + } + } + StackLayout { + Layout.fillWidth: true + currentIndex: tabBar.currentIndex + // Tokens layout definition: + ColumnLayout { + Layout.fillWidth: true + Layout.fillHeight: true + StatusPickerButton { + Layout.fillWidth: true + Layout.topMargin: 8 + Layout.preferredHeight: 36 + bgColor: Theme.palette.baseColor5 + contentColor: Theme.palette.directColor1 + text: root.tokenName + font.pixelSize: 13 + image.source: root.tokenImage + onClicked: loader.sourceComponent = tokensExtendedView + } + + StatusInput { + Layout.fillWidth: true + minimumHeight: 36 + maximumHeight: 36 + topPadding: 0 + bottomPadding: 0 + text: root.tokenAmountValue == 0 ? "" : root.tokenAmountValue.toString() + font.pixelSize: 13 + rightPadding: amountText.implicitWidth + amountText.anchors.rightMargin + leftPadding + input.placeholderText: "0" + validationMode: StatusInput.ValidationMode.IgnoreInvalidInput + validators: StatusFloatValidator { bottom: 0 } + + StatusBaseText { + id: amountText + anchors.right: parent.right + anchors.rightMargin: 13 + anchors.verticalCenter: parent.verticalCenter + text: qsTr("Amount") + color: Theme.palette.baseColor1 + font.pixelSize: 13 + } + onTextChanged: root.tokenAmountValue = Number(text) + } + + // Just a filler + Item { Layout.fillHeight: true} + + StatusButton { + enabled: d.ready + text: qsTr("Add") + height: 44 + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + onClicked: { root.addToken(root.tokenAmountValue.toString() + " " + root.tokenName, root.tokenImage, root.operator) } + } + } // End of Tokens Layout definition + + // TODO + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + + // TODO + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + } + } + } + + Component { + id: operatorsSelectorView + ColumnLayout { + anchors.fill: parent + anchors.margins: 8 + StatusPickerButton { + Layout.fillWidth: true + Layout.preferredHeight: 36 + horizontalPadding: 12 + spacing: 10 + bgColor: Theme.palette.primaryColor3 + contentColor: Theme.palette.primaryColor1 + image.source: Style.svg("add") + text: qsTr("And...") + image.height: 12 + image.width: 12 + font.pixelSize: 13 + onClicked: { + root.operator = SQ.Utils.Operators.And + loader.sourceComponent = tabsView + } + } + StatusPickerButton { + Layout.fillWidth: true + Layout.preferredHeight: 36 + horizontalPadding: 12 + spacing: 10 + bgColor: Theme.palette.primaryColor3 + contentColor: Theme.palette.primaryColor1 + image.source: Style.svg("condition-Or") + image.height: 12 + image.width: 12 + text: qsTr("Or...") + font.pixelSize: 13 + onClicked: { + root.operator = SQ.Utils.Operators.Or + loader.sourceComponent = tabsView + } + } + } + } + + Component { + id: tokensExtendedView + + // TODO: It probabily will be a reusable component for collectibles and channels + TokensListDropdownContent { + anchors.fill: parent + anchors.topMargin: 8 + anchors.bottomMargin: 8 + headerModel: ListModel { + ListElement { index: 0; icon: "next"; iconSize: 12; description: qsTr("Back"); rotation: 180; spacing: 0 } + ListElement { index: 1; icon: "add"; iconSize: 16; description: qsTr("Mint token"); rotation: 0; spacing: 8 } + ListElement { index: 2; icon: "invite-users"; iconSize: 16; description: qsTr("Import existing token"); rotation: 180; spacing: 8 } + } + // TODO: Replace to real data, now dummy model + model: ListModel { + ListElement {imageSource: "qrc:imports/assets/png/tokens/SOCKS.png"; name: "Unisocks"; shortName: "SOCKS"; selected: false; category: "Community tokens"} + ListElement {imageSource: "qrc:imports/assets/png/tokens/ZRX.png"; name: "Ox"; shortName: "ZRX"; selected: false; category: "Listed tokens"} + ListElement {imageSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "1inch"; shortName: "ZRX"; selected: false; category: "Listed tokens"} + ListElement {imageSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "Aave"; shortName: "AAVE"; selected: false; category: "Listed tokens"} + ListElement {imageSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "Amp"; shortName: "AMP"; selected: false; category: "Listed tokens"} + } + onHeaderItemClicked: { + if(index === 0) loader.sourceComponent = tabsView // Go back + // TODO: + else if(index === 1) console.log("TODO: Mint token") + else if(index === 2) console.log("TODO: Import existing token") + } + onItemClicked: { + // Go back + loader.sourceComponent = tabsView + + // Update new token info + root.tokenName = shortName + root.tokenImage = imageSource + } + } + } +} diff --git a/ui/app/AppLayouts/Chat/controls/community/TokensListDropdownContent.qml b/ui/app/AppLayouts/Chat/controls/community/TokensListDropdownContent.qml new file mode 100644 index 0000000000..0b70251cd0 --- /dev/null +++ b/ui/app/AppLayouts/Chat/controls/community/TokensListDropdownContent.qml @@ -0,0 +1,113 @@ +import QtQuick 2.13 +import QtQuick.Layouts 1.14 +import QtQuick.Controls 2.13 +import QtGraphicalEffects 1.13 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Controls 0.1 +import StatusQ.Components 0.1 + + +StatusListView { + id: root + + property var headerModel + + signal headerItemClicked(int index) + signal itemClicked(string name, string shortName, url imageSource) + + implicitWidth: 273 + currentIndex: -1 + clip: true + headerPositioning: ListView.OverlayHeader + header: Rectangle { + z: 3 // Above delegate (z=1) and above section.delegate (z = 2) + color: Theme.palette.statusPopupMenu.backgroundColor + width: root.width + height: columnHeader.implicitHeight + 2 * columnHeader.anchors.topMargin + ColumnLayout { + id: columnHeader + anchors.top: parent.top + anchors.left: parent.left + anchors.leftMargin: 16 + anchors.rightMargin: anchors.leftMargin + anchors.topMargin: 8 + anchors.bottomMargin: 2 * anchors.topMargin + spacing: 20 + Repeater { + model: root.headerModel + delegate: StatusIconTextButton { + z: 3 // Above delegate (z=1) and above section.delegate (z = 2) + spacing: model.spacing + statusIcon: model.icon + icon.width: model.iconSize + icon.height: model.iconSize + iconRotation: model.rotation + text: model.description + onClicked: root.headerItemClicked(model.index) + } + } + } + }// End of Header + delegate: Rectangle { + width: ListView.view.width + height: 44 // by design + color: mouseArea.containsMouse ? Theme.palette.baseColor4 : "transparent" + RowLayout { + anchors.fill: parent + anchors.leftMargin: 14 + spacing: 8 + StatusRoundedImage { + Layout.alignment: Qt.AlignVCenter + image.source: model.imageSource + visible: model.imageSource.toString() !== "" + Layout.preferredWidth: 28 + Layout.preferredHeight: Layout.preferredWidth + } + ColumnLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + spacing: 0 + StatusBaseText { + Layout.fillWidth: true + text: model.name + color: Theme.palette.directColor1 + font.pixelSize: 13 + clip: true + elide: Text.ElideRight + } + StatusBaseText { + Layout.fillWidth: true + text: model.shortName + color: Theme.palette.baseColor1 + font.pixelSize: 12 + clip: true + elide: Text.ElideRight + } + } + } + MouseArea { + id: mouseArea + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + hoverEnabled: true + onClicked: { root.itemClicked(model.name, model.shortName, model.imageSource) } + } + }// End of Item + section.property: "category" + section.criteria: ViewSection.FullString + section.delegate: Item { + width: ListView.view.width + height: 34 // by design + StatusBaseText { + anchors.leftMargin: 18 + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + text: section + color: Theme.palette.baseColor1 + font.pixelSize: 12 + elide: Text.ElideRight + } + }// End of Category item +}// End of Root diff --git a/ui/app/AppLayouts/Chat/layouts/SettingsPageLayout.qml b/ui/app/AppLayouts/Chat/layouts/SettingsPageLayout.qml index 6434077400..01fdab803d 100644 --- a/ui/app/AppLayouts/Chat/layouts/SettingsPageLayout.qml +++ b/ui/app/AppLayouts/Chat/layouts/SettingsPageLayout.qml @@ -5,6 +5,8 @@ import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 import StatusQ.Controls 0.1 +import utils 1.0 +import shared.panels 1.0 import shared.popups 1.0 Item { @@ -44,24 +46,18 @@ Item { id: layout anchors.fill: parent - spacing: 16 - Item { + StatusIconTextButton { implicitHeight: 32 - - StatusBaseText { - visible: root.previousPage - text: "<- " + root.previousPage - color: Theme.palette.primaryColor1 - font.pixelSize: 15 - - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: root.previousPageClicked() - } - } + visible: root.previousPage + spacing: 8 + statusIcon: "arrow" + icon.width: 24 + icon.height: 24 + text: root.previousPage + font.pixelSize: 15 + onClicked: root.previousPageClicked() } StatusBaseText { @@ -77,6 +73,7 @@ Item { id: contentLoader Layout.fillWidth: true Layout.fillHeight: true + Layout.topMargin: 16 Layout.leftMargin: 24 Layout.rightMargin: 24 @@ -98,4 +95,3 @@ Item { onSaveChangesClicked: root.saveChangesClicked() } } - diff --git a/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml b/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml new file mode 100644 index 0000000000..343d195750 --- /dev/null +++ b/ui/app/AppLayouts/Chat/panels/communities/CommunityPermissionsSettingsPanel.qml @@ -0,0 +1,50 @@ +import QtQuick 2.14 + +import "../../layouts" +import "../../views/communities" + +SettingsPageLayout { + id: root + + QtObject { + id: d + + readonly property string welcomeViewState: "WELCOME" + readonly property string newPermissionViewState: "NEWPERMISSION" + } + + state: d.welcomeViewState // Initial state + states: [ + State { + name: d.welcomeViewState + PropertyChanges {target: root; title: qsTr("Permissions")} + PropertyChanges {target: root; previousPage: ""} + PropertyChanges {target: root; content: welcomeView} + }, + State { + name: d.newPermissionViewState + PropertyChanges {target: root; title: qsTr("New permission")} + PropertyChanges {target: root; previousPage: qsTr("Permissions")} + PropertyChanges {target: root; content: newPermissionView} + } + ] + + onPreviousPageClicked: { + if(root.state === d.newPermissionViewState) { + root.state = d.welcomeViewState + } + } + + // Community Permissions possible view contents: + Component { + id: welcomeView + CommunityWelcomePermissionsView { + onAddPermission: root.state = d.newPermissionViewState + } + } + + Component { + id: newPermissionView + CommunityNewPermissionView { } + } +} diff --git a/ui/app/AppLayouts/Chat/stores/RootStore.qml b/ui/app/AppLayouts/Chat/stores/RootStore.qml index 17d03fe760..5fadaf1ef4 100644 --- a/ui/app/AppLayouts/Chat/stores/RootStore.qml +++ b/ui/app/AppLayouts/Chat/stores/RootStore.qml @@ -126,6 +126,7 @@ QtObject { property var communitiesModuleInst: communitiesModule property var communitiesList: communitiesModuleInst.model + property bool communityPermissionsEnabled: localAccountSensitiveSettings.isCommunityPermissionsEnabled property var userProfileInst: userProfile diff --git a/ui/app/AppLayouts/Chat/views/CommunitySettingsView.qml b/ui/app/AppLayouts/Chat/views/CommunitySettingsView.qml index f954dc3ca4..51f237f478 100644 --- a/ui/app/AppLayouts/Chat/views/CommunitySettingsView.qml +++ b/ui/app/AppLayouts/Chat/views/CommunitySettingsView.qml @@ -23,17 +23,18 @@ StatusAppTwoPanelLayout { id: root // TODO: get this model from backend? - property var settingsMenuModel: [ - {name: qsTr("Overview"), icon: "help"}, - {name: qsTr("Members"), icon: "group-chat"}, -// {name: qsTr("Permissions"), icon: "objects"}, -// {name: qsTr("Tokens"), icon: "token"}, -// {name: qsTr("Airdrops"), icon: "airdrop"}, -// {name: qsTr("Token sales"), icon: "token-sale"}, -// {name: qsTr("Subscriptions"), icon: "subscription"} - ] + property var settingsMenuModel: root.rootStore.communityPermissionsEnabled ? [{name: qsTr("Overview"), icon: "help"}, + {name: qsTr("Members"), icon: "group-chat"}, + {name: qsTr("Permissions"), icon: "objects"}] : + [{name: qsTr("Overview"), icon: "help"}, + {name: qsTr("Members"), icon: "group-chat"}] + // TODO: Next community settings options: + // {name: qsTr("Tokens"), icon: "token"}, + // {name: qsTr("Airdrops"), icon: "airdrop"}, + // {name: qsTr("Token sales"), icon: "token-sale"}, + // {name: qsTr("Subscriptions"), icon: "subscription"}, - property var rootStore + property var rootStore property var community property var chatCommunitySectionModule property bool hasAddedContacts: false @@ -122,12 +123,8 @@ StatusAppTwoPanelLayout { rightPanel: Loader { anchors.fill: parent - anchors.leftMargin: 28 - anchors.topMargin: 23 - anchors.margins: 16 - + anchors.margins: 32 active: root.community - sourceComponent: StackLayout { currentIndex: d.currentIndex @@ -199,9 +196,12 @@ StatusAppTwoPanelLayout { communitySectionModule: root.chatCommunitySectionModule }) } + + CommunityPermissionsSettingsPanel {} } } + onSettingsMenuModelChanged: d.currentIndex = 0 QtObject { id: d diff --git a/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml b/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml new file mode 100644 index 0000000000..ae993856f5 --- /dev/null +++ b/ui/app/AppLayouts/Chat/views/communities/CommunityNewPermissionView.qml @@ -0,0 +1,127 @@ +import QtQuick 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 utils 1.0 +import shared.panels 1.0 + +import "../../../Chat/controls/community" + +Flickable { + id: root + + signal createPermission() + + QtObject { + id: d + property bool isPrivate: false + } + + contentWidth: mainLayout.width + contentHeight: mainLayout.height + clip: true + flickableDirection: Flickable.AutoFlickIfNeeded + + ColumnLayout { + id: mainLayout + width: 560 // by design + spacing: 0 + CurveSeparatorWithText { + Layout.alignment: Qt.AlignLeft + Layout.leftMargin: 14 + text: qsTr("Anyone") + } + StatusItemSelector { + id: tokensSelector + Layout.fillWidth: true + icon: Style.svg("contact_verified") + title: qsTr("Who holds") + defaultItemText: qsTr("Example: 10 SNT") + andOperatorText: qsTr("and") + orOperatorText: qsTr("or") + popupItem: HoldingsDropdown { + id: dropdown + withOperatorSelector: tokensSelector.itemsModel.count > 0 + onAddToken: { + tokensSelector.addItem(tokenText, tokenImage, operator) + dropdown.close() + } + } + } + Rectangle { + Layout.leftMargin: 16 + Layout.preferredWidth: 2 + Layout.preferredHeight: 24 + color: Style.current.separator + } + StatusItemSelector { + Layout.fillWidth: true + icon: Style.svg("profile/security") + iconSize: 24 + title: qsTr("Is allowed to") + defaultItemText: qsTr("Example: View and post") + } + Rectangle { + Layout.leftMargin: 16 + Layout.preferredWidth: 2 + Layout.preferredHeight: 24 + color: Style.current.separator + } + StatusItemSelector { + Layout.fillWidth: true + icon: Style.svg("create-category") + iconSize: 24 + title: qsTr("In") + defaultItemText: qsTr("Example: `#general` channel") + } + Separator { + Layout.topMargin: 24 + } + RowLayout { + Layout.topMargin: 12 + Layout.fillWidth: true + Layout.leftMargin: 16 + Layout.rightMargin: Layout.leftMargin + spacing: 16 + StatusRoundIcon { + icon.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 don’t meet it’s requirements") + color: Theme.palette.baseColor1 + font.pixelSize: 15 + lineHeight: 1.2 + wrapMode: Text.WordWrap + elide: Text.ElideRight + clip: true + } + } + StatusSwitch { + checked: d.isPrivate + onToggled: { d.isPrivate = checked } + } + } + StatusButton { + Layout.topMargin: 24 + text: qsTr("Create permission") + enabled: false + Layout.preferredHeight: 44 + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + onClicked: root.createPermission() + } + } +} diff --git a/ui/app/AppLayouts/Chat/views/communities/CommunityWelcomePermissionsView.qml b/ui/app/AppLayouts/Chat/views/communities/CommunityWelcomePermissionsView.qml new file mode 100644 index 0000000000..473f434ae6 --- /dev/null +++ b/ui/app/AppLayouts/Chat/views/communities/CommunityWelcomePermissionsView.qml @@ -0,0 +1,139 @@ +import QtQuick 2.14 +import QtQuick.Layouts 1.14 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 +import StatusQ.Controls 0.1 + +import utils 1.0 + +Flickable { + id: root + + signal addPermission() + + contentWidth: mainLayout.width + contentHeight: mainLayout.height + mainLayout.anchors.topMargin + clip: true + flickableDirection: Flickable.AutoFlickIfNeeded + + ColumnLayout { + id: mainLayout + width: 560 // by design + spacing: 24 + + Rectangle { + Layout.fillWidth: true + Layout.preferredHeight: contentColumn.implicitHeight + contentColumn.anchors.topMargin + contentColumn.anchors.bottomMargin + color: "transparent" + radius: 16 + border.color: Theme.palette.baseColor5 + clip: true + + ColumnLayout { + id: contentColumn + anchors.fill: parent + anchors.margins: 16 + anchors.bottomMargin: 32 + spacing: 8 + clip: true + Image { + Layout.preferredWidth: 257 + Layout.preferredHeight: Layout.preferredWidth + Layout.alignment: Qt.AlignHCenter + source: Style.png("community/permissions21_3_1") + mipmap: true + } + StatusBaseText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + text: qsTr("Permissions") + font.pixelSize: 17 + font.weight: Font.Bold + color: Theme.palette.directColor1 + } + StatusBaseText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + text: qsTr("You can manage your community by creating and issuing membership and access permissions") + lineHeight: 1.2 + font.pixelSize: 15 + color: Theme.palette.baseColor1 + wrapMode: Text.WordWrap + } + ColumnLayout { + id: checkersColumn + property int rowChildSpacing: 10 + property color rowIconColor: Theme.palette.primaryColor1 + property string rowIconName: "checkmark-circle" + property int rowFontSize: 15 + property color rowTextColor: Theme.palette.directColor1 + property double rowTextLineHeight: 1.2 + + Layout.fillWidth: true + Layout.alignment: Qt.AlignLeft + spacing: 10 + RowLayout { + Layout.fillWidth: true + spacing: checkersColumn.rowChildSpacing + StatusIcon { + icon: checkersColumn.rowIconName + color: checkersColumn.rowIconColor + } + StatusBaseText { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + text: qsTr("Give individual members access to private channels") + lineHeight: checkersColumn.rowTextLineHeight + font.pixelSize: checkersColumn.rowFontSize + color: checkersColumn.rowTextColor + wrapMode: Text.WordWrap + } + } + RowLayout { + Layout.fillWidth: true + spacing: checkersColumn.rowChildSpacing + StatusIcon { + icon: checkersColumn.rowIconName + color: checkersColumn.rowIconColor + } + StatusBaseText { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + text: qsTr("Monetise your community with subscriptions and fees") + lineHeight: checkersColumn.rowTextLineHeight + font.pixelSize: checkersColumn.rowFontSize + color: Theme.palette.directColor1 + wrapMode: Text.WordWrap + } + } + RowLayout { + Layout.fillWidth: true + spacing: checkersColumn.rowChildSpacing + StatusIcon { + icon: checkersColumn.rowIconName + color: checkersColumn.rowIconColor + } + StatusBaseText { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + text: qsTr("Require holding a token or NFT to obtain exclusive membership rights") + lineHeight: checkersColumn.rowTextLineHeight + font.pixelSize: checkersColumn.rowFontSize + color: checkersColumn.rowTextColor + wrapMode: Text.WordWrap + } + } + } + } + } + + StatusButton { + text: qsTr("Add permission") + height: 44 + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + onClicked: root.addPermission() + } + } +} diff --git a/ui/app/AppLayouts/Profile/stores/AdvancedStore.qml b/ui/app/AppLayouts/Profile/stores/AdvancedStore.qml index 3f9abe64c4..82dd6d8576 100644 --- a/ui/app/AppLayouts/Profile/stores/AdvancedStore.qml +++ b/ui/app/AppLayouts/Profile/stores/AdvancedStore.qml @@ -33,6 +33,7 @@ QtObject { readonly property string gifWidget: "gifWidget" readonly property string communityHistoryArchiveSupport: "communityHistoryArchiveSupport" readonly property string communitiesPortal: "communitiesPortal" + readonly property string communityPermissions: "communityPermissions" } function logDir() { @@ -130,5 +131,8 @@ QtObject { else if (feature === experimentalFeatures.gifWidget) { localAccountSensitiveSettings.isGifWidgetEnabled = !localAccountSensitiveSettings.isGifWidgetEnabled } + else if (feature === experimentalFeatures.communityPermissions) { + localAccountSensitiveSettings.isCommunityPermissionsEnabled = !localAccountSensitiveSettings.isCommunityPermissionsEnabled + } } } diff --git a/ui/app/AppLayouts/Profile/views/AdvancedView.qml b/ui/app/AppLayouts/Profile/views/AdvancedView.qml index aad789efca..709006670b 100644 --- a/ui/app/AppLayouts/Profile/views/AdvancedView.qml +++ b/ui/app/AppLayouts/Profile/views/AdvancedView.qml @@ -163,6 +163,23 @@ SettingsContentBase { } } + // TODO: replace with StatusQ component + StatusSettingsLineButton { + anchors.leftMargin: 0 + anchors.rightMargin: 0 + text: qsTr("Community Permissions Settings") + isSwitch: true + switchChecked: localAccountSensitiveSettings.isCommunityPermissionsEnabled + onClicked: { + if (!localAccountSensitiveSettings.isCommunityPermissionsEnabled) { + confirmationPopup.experimentalFeature = root.advancedStore.experimentalFeatures.communityPermissions + confirmationPopup.open() + } else { + root.advancedStore.toggleExperimentalFeature(root.advancedStore.experimentalFeatures.communityPermissions) + } + } + } + StatusSectionHeadline { anchors.left: parent.left anchors.right: parent.right diff --git a/ui/imports/assets/icons/add.svg b/ui/imports/assets/icons/add.svg new file mode 100644 index 0000000000..6c147debd0 --- /dev/null +++ b/ui/imports/assets/icons/add.svg @@ -0,0 +1,3 @@ + + + diff --git a/ui/imports/assets/icons/condition-Or.svg b/ui/imports/assets/icons/condition-Or.svg new file mode 100644 index 0000000000..c0b38a1a97 --- /dev/null +++ b/ui/imports/assets/icons/condition-Or.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/ui/imports/assets/icons/contact_verified.svg b/ui/imports/assets/icons/contact_verified.svg new file mode 100644 index 0000000000..e0305776f8 --- /dev/null +++ b/ui/imports/assets/icons/contact_verified.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/ui/imports/assets/png/community/permissions21_3_1.png b/ui/imports/assets/png/community/permissions21_3_1.png new file mode 100644 index 0000000000..7776721c86 Binary files /dev/null and b/ui/imports/assets/png/community/permissions21_3_1.png differ diff --git a/ui/imports/shared/panels/CurveSeparatorWithText.qml b/ui/imports/shared/panels/CurveSeparatorWithText.qml new file mode 100644 index 0000000000..979ddce1fc --- /dev/null +++ b/ui/imports/shared/panels/CurveSeparatorWithText.qml @@ -0,0 +1,45 @@ +import QtQuick 2.14 +import QtQuick.Layouts 1.14 + +import StatusQ.Core 0.1 +import StatusQ.Core.Theme 0.1 + +import utils 1.0 + +RowLayout { + id: root + + property string text + + implicitHeight: 32 + + Canvas { + readonly property int cornerRadius: 9 + readonly property int lineWidth: 2 + readonly property int verticalLine: 12 + readonly property int horizontalLine: 19 + + Layout.preferredWidth: horizontalLine + cornerRadius + Layout.preferredHeight: verticalLine + lineWidth + cornerRadius + Layout.alignment: Qt.AlignTop + Layout.topMargin: 12 + contextType: "2d" + onPaint: { + context.reset(); + context.beginPath() + context.moveTo(width, lineWidth) + context.arc(cornerRadius + lineWidth, cornerRadius + lineWidth, cornerRadius, 3 * Math.PI / 2, Math.PI, true/*anticlockwise*/) + context.lineTo(lineWidth, cornerRadius + verticalLine + lineWidth) + context.strokeStyle = Style.current.separator + context.lineWidth = 2 + context.stroke() + } + } + StatusBaseText { + Layout.alignment: Qt.AlignTop + Layout.topMargin: 2 + text: root.text + color: Theme.palette.directColor1 + font.pixelSize: 17 + } +} diff --git a/ui/imports/shared/panels/qmldir b/ui/imports/shared/panels/qmldir index ce47ce796f..271ee1b746 100644 --- a/ui/imports/shared/panels/qmldir +++ b/ui/imports/shared/panels/qmldir @@ -12,6 +12,7 @@ RoundedIcon 1.0 RoundedIcon.qml RoundedImage 1.0 RoundedImage.qml Separator 1.0 Separator.qml SeparatorWithIcon 1.0 SeparatorWithIcon.qml +CurveSeparatorWithText 1.0 CurveSeparatorWithText.qml SplitViewHandle 1.0 SplitViewHandle.qml StyledText 1.0 StyledText.qml SVGImage 1.0 SVGImage.qml