feat(CommunitySettings / Permissions): Added `welcome` page and `How holds` tokens dropdown
- Added functionality as experimental advanced view button feat(CommunitySettings / Permissions): Added welcome page - Enabled new permissions tab. - Created welcome page layout. - Added permissions welcome image. - Fixed top margin content in `SettingsPanelLayout` to fit designs. Closes #6036 feat(CommunitySettings/Permissions): Created `new permission` page - Added `new permission` page. - Created first card layout. - Added navigation between `welcome` and `newPermission` views. - Improvements in base community settings layout pages. Closes #6037 feat(CommunitySettings/Permissions): `Who holds` tokens dropdown component creation and integration - Tokens dropdown component creation: main view, operators view and extended view. - Logic to add new token and change operator. Part of #6038
This commit is contained in:
parent
5fb4c7f110
commit
7b54bf31b4
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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,25 +46,19 @@ Item {
|
|||
id: layout
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
spacing: 16
|
||||
|
||||
Item {
|
||||
StatusIconTextButton {
|
||||
implicitHeight: 32
|
||||
|
||||
StatusBaseText {
|
||||
visible: root.previousPage
|
||||
text: "<- " + root.previousPage
|
||||
color: Theme.palette.primaryColor1
|
||||
spacing: 8
|
||||
statusIcon: "arrow"
|
||||
icon.width: 24
|
||||
icon.height: 24
|
||||
text: root.previousPage
|
||||
font.pixelSize: 15
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.previousPageClicked()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
Layout.leftMargin: 36
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 { }
|
||||
}
|
||||
}
|
|
@ -126,6 +126,7 @@ QtObject {
|
|||
|
||||
property var communitiesModuleInst: communitiesModule
|
||||
property var communitiesList: communitiesModuleInst.model
|
||||
property bool communityPermissionsEnabled: localAccountSensitiveSettings.isCommunityPermissionsEnabled
|
||||
|
||||
property var userProfileInst: userProfile
|
||||
|
||||
|
|
|
@ -23,15 +23,16 @@ StatusAppTwoPanelLayout {
|
|||
id: root
|
||||
|
||||
// TODO: get this model from backend?
|
||||
property var settingsMenuModel: [
|
||||
{name: qsTr("Overview"), icon: "help"},
|
||||
property var settingsMenuModel: root.rootStore.communityPermissionsEnabled ? [{name: qsTr("Overview"), icon: "help"},
|
||||
{name: qsTr("Members"), icon: "group-chat"},
|
||||
// {name: qsTr("Permissions"), icon: "objects"},
|
||||
{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"}
|
||||
]
|
||||
// {name: qsTr("Subscriptions"), icon: "subscription"},
|
||||
|
||||
property var rootStore
|
||||
property var community
|
||||
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="10" height="10" viewBox="0 0 10 10" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M5 0.5C5.27614 0.5 5.5 0.723858 5.5 1V4.16667C5.5 4.35076 5.64924 4.5 5.83333 4.5H9C9.27614 4.5 9.5 4.72386 9.5 5C9.5 5.27614 9.27614 5.5 9 5.5H5.83333C5.64924 5.5 5.5 5.64924 5.5 5.83333V9C5.5 9.27614 5.27614 9.5 5 9.5C4.72386 9.5 4.5 9.27614 4.5 9V5.83333C4.5 5.64924 4.35076 5.5 4.16667 5.5H1C0.723858 5.5 0.5 5.27614 0.5 5C0.5 4.72386 0.723858 4.5 1 4.5H4.16667C4.35076 4.5 4.5 4.35076 4.5 4.16667V1C4.5 0.723858 4.72386 0.5 5 0.5Z" fill="#4360DF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 566 B |
|
@ -0,0 +1,5 @@
|
|||
<svg width="14" height="12" viewBox="0 0 14 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M7.99838 0.00722752C7.70764 -0.0407138 7.43287 0.154896 7.38468 0.444134L5.5622 11.3825C5.51401 11.6718 5.71063 11.9451 6.00138 11.993C6.29212 12.041 6.56688 11.8454 6.61508 11.5561L8.43756 0.617744C8.48575 0.328507 8.28913 0.0551688 7.99838 0.00722752Z" fill="#4360DF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3333 3.5C9.95254 3.5 8.83325 4.61929 8.83325 6C8.83325 7.38071 9.95254 8.5 11.3333 8.5C12.714 8.5 13.8333 7.38071 13.8333 6C13.8333 4.61929 12.714 3.5 11.3333 3.5ZM9.83325 6C9.83325 6.82843 10.5048 7.5 11.3333 7.5C12.1617 7.5 12.8333 6.82843 12.8333 6C12.8333 5.17157 12.1617 4.5 11.3333 4.5C10.5048 4.5 9.83325 5.17157 9.83325 6Z" fill="#4360DF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M1.66659 3.66667C0.930206 3.66667 0.333252 4.26362 0.333252 5V7C0.333252 7.73638 0.930206 8.33333 1.66659 8.33333H3.66659C4.40297 8.33333 4.99992 7.73638 4.99992 7V5C4.99992 4.26362 4.40297 3.66667 3.66659 3.66667H1.66659ZM3.66659 4.66667H1.66659C1.48249 4.66667 1.33325 4.81591 1.33325 5V7C1.33325 7.18409 1.48249 7.33333 1.66659 7.33333H3.66659C3.85068 7.33333 3.99992 7.18409 3.99992 7V5C3.99992 4.81591 3.85068 4.66667 3.66659 4.66667Z" fill="#4360DF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
|
@ -0,0 +1,5 @@
|
|||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 10C10.7614 10 13 7.76142 13 5C13 2.23858 10.7614 0 8 0C5.23858 0 3 2.23858 3 5C3 7.76142 5.23858 10 8 10ZM8 8.5C9.933 8.5 11.5 6.933 11.5 5C11.5 3.067 9.933 1.5 8 1.5C6.067 1.5 4.5 3.067 4.5 5C4.5 6.933 6.067 8.5 8 8.5Z" fill="#4360DF"/>
|
||||
<path d="M8.93455 13.7839C8.89975 14.2015 8.52884 14.506 8.10986 14.5007C8.07325 14.5002 8.03657 14.5 7.99985 14.5C5.45645 14.5 3.17395 15.6171 1.61624 17.3874C1.31374 17.7312 0.777725 17.7779 0.453922 17.454C0.193192 17.1933 0.161813 16.7784 0.401673 16.4984C2.23574 14.357 4.95932 13 7.99985 13C8.08678 13 8.17346 13.0011 8.25987 13.0033C8.66888 13.0138 8.96852 13.3762 8.93455 13.7839Z" fill="#4360DF"/>
|
||||
<path d="M17.6708 10.3354C17.8561 9.96493 17.7059 9.51442 17.3354 9.32918C16.9649 9.14394 16.5144 9.29411 16.3292 9.66459L14.1048 14.1134C13.9522 14.4186 13.5453 14.4847 13.304 14.2433L12.5303 13.4697C12.2374 13.1768 11.7626 13.1768 11.4697 13.4697C11.1768 13.7626 11.1768 14.2374 11.4697 14.5303L12.9791 16.0398C13.5824 16.6431 14.5995 16.4781 14.9811 15.7149L17.6708 10.3354Z" fill="#4360DF"/>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 24 KiB |
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue