feat(CommunityPermission): Added `How holds` collectibles dropdown

- Added collectibles tab component.
- Added extended dropdown base content component
- Added filter popup option.
- Added collectibles model
- Added subitems in model
- Added thumbnails mode view
- Created `CommunitiesStore` and moved mock data to there.
- Added forward subitems navigation and selection.
- Updated `statusq`.

Closes #6337
This commit is contained in:
Noelia 2022-08-23 10:46:37 +02:00 committed by Noelia
parent 2bbd9f4363
commit 3d6e40fd79
16 changed files with 715 additions and 176 deletions

@ -1 +1 @@
Subproject commit 01624316c2349f9179514c06a84e5e941823a2fc
Subproject commit db0f8e9fb53863998403e3f6e7b3a2a62f173b4f

View File

@ -0,0 +1,277 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.14
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Popups 0.1
import StatusQ.Core 0.1
import shared.panels 1.0
ColumnLayout {
id: root
property var store
property int type: ExtendedDropdownContent.Type.Tokens
signal goBack()
signal itemClicked(string key, string name, url iconSource)
enum Type{
Tokens,
Collectibles
}
QtObject {
id: d
readonly property int filterItemsHeight: 36
readonly property int filterPopupWidth: 233
readonly property int filterPopupHeight: 205
// Internal management properties
property bool isFilterOptionVisible: false
readonly property string thumbnailsViewState: "THUMBNAILS"
readonly property string listView_depth1_State: "LIST-DEPTH1"
readonly property string listView_depth2_State: "LIST-DEPTH2"
property var currentModel: root.store.collectiblesModel
property var currentSubitems
property string currentItemName: ""
property url currentItemSource: ""
function reset() {
d.currentItemName = ""
d.currentItemSource = ""
d.currentModel = root.store.collectiblesModel
d.currentSubitems = undefined
root.state = d.listView_depth1_State
}
}
spacing: 0
state: d.listView_depth1_State
states: [
State {
name: d.thumbnailsViewState
PropertyChanges {target: contentLoader; sourceComponent: thumbnailsView}
PropertyChanges {target: d; isFilterOptionVisible: true}
PropertyChanges {target: d; currentModel: d.currentSubitems}
},
State {
name: d.listView_depth1_State
PropertyChanges {target: contentLoader; sourceComponent: root.type === ExtendedDropdownContent.Type.Tokens ? tokensListView : collectiblesListView}
PropertyChanges {target: d; isFilterOptionVisible: false}
PropertyChanges {target: d; currentModel: root.type === ExtendedDropdownContent.Type.Tokens ? root.store.tokensModel : root.store.collectiblesModel}
},
State {
name: d.listView_depth2_State
PropertyChanges {target: contentLoader; sourceComponent: collectiblesListView}
PropertyChanges {target: d; isFilterOptionVisible: true }
PropertyChanges {target: d; currentModel: d.currentSubitems}
}
]
// Header
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 8
Layout.topMargin: 5
StatusIconTextButton {
Layout.alignment: Qt.AlignVCenter
spacing: 0
statusIcon: "next"
icon.width: 12
icon.height: 12
iconRotation: 180
text: qsTr("Back")
onClicked: {
if(root.state == d.listView_depth1_State) {
root.goBack()
}
else {
root.state = d.listView_depth1_State
}
}
}
// Just a filler to fit layout
Item { Layout.fillWidth: true; height: filterButton.implicitHeight }
StatusFlatRoundButton {
id: filterButton
implicitWidth: 32
implicitHeight: 32
visible: d.isFilterOptionVisible
type: StatusFlatRoundButton.Type.Secondary
icon.name: "filter"
onClicked: {
filterOptionsPopup.x = filterButton.x + filterButton.width - filterOptionsPopup.width
filterOptionsPopup.y = filterButton.y + filterButton.height + 8
filterOptionsPopup.open()
}
}
// Filter options popup:
StatusDropdown {
id: filterOptionsPopup
width: d.filterPopupWidth
height: d.filterPopupHeight
contentItem: ColumnLayout {
anchors.fill: parent
anchors.topMargin: 8
anchors.bottomMargin: 8
ListView {
Layout.fillWidth: true
Layout.preferredHeight: model.count * d.filterItemsHeight
model: ListModel {
ListElement { text: qsTr("Most viewed"); selected: true }
ListElement { text: qsTr("Newest first"); selected: false }
ListElement { text: qsTr("Oldest first"); selected: false }
}
delegate: StatusItemPicker {
width: ListView.view.width
height: d.filterItemsHeight
color: sensor1.containsMouse ? Theme.palette.baseColor4 : "transparent"
name: model.text
namePixelSize: 13
selectorType: StatusItemPicker.SelectorType.RadioButton
radioGroup: filterRadioBtnGroup
radioButtonSize: StatusRadioButton.Size.Small
selected: model.selected
MouseArea {
id: sensor1
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
selected = !selected
console.log("TODO: Clicked filter option: " + model.text)
filterOptionsPopup.close()
}
}
}
// Not visual element to control filter options
ButtonGroup {
id: filterRadioBtnGroup
}
}
Separator { Layout.fillWidth: true }
ListView {
Layout.fillWidth: true
Layout.preferredHeight: model.count * d.filterItemsHeight
model: ListModel {
ListElement { key: "LIST"; text: qsTr("List"); selected: true }
ListElement { key: "THUMBNAILS"; text: qsTr("Thumbnails"); selected: false }
}
delegate: StatusItemPicker {
width: ListView.view.width
height: d.filterItemsHeight
color: sensor2.containsMouse ? Theme.palette.baseColor4 : "transparent"
name: model.text
namePixelSize: 13
selectorType: StatusItemPicker.SelectorType.RadioButton
radioGroup: visualizationRadioBtnGroup
radioButtonSize: StatusRadioButton.Size.Small
selected: model.selected
MouseArea {
id: sensor2
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: {
selected = !selected
if(model.key === "LIST") {
root.state = d.listView_depth2_State
}
else if(model.key === "THUMBNAILS") {
root.state = d.thumbnailsViewState
}
filterOptionsPopup.close()
}
}
}
// Not visual element to control visualization options
ButtonGroup {
id: visualizationRadioBtnGroup
}
}
}
}
}
// List elements content
Loader {
id: contentLoader
Layout.preferredWidth: 289 // by design
Layout.bottomMargin: 5
Layout.preferredHeight: (item != null && typeof(item) !== 'undefined') ? item.implicitHeight : 0
}
Component {
id: tokensListView
ListDropdownContent {
headerModel: ListModel {
ListElement { key: "MINT"; icon: "add"; iconSize: 16; description: qsTr("Mint token"); rotation: 0; spacing: 8 }
ListElement { key: "IMPORT"; icon: "invite-users"; iconSize: 16; description: qsTr("Import existing token"); rotation: 180; spacing: 8 }
}
model: d.currentModel
onHeaderItemClicked: {
if(key === "MINT") console.log("TODO: Mint token")
else if(key === "IMPORT") console.log("TODO: Import existing token")
}
onItemClicked: root.itemClicked(key, shortName, iconSource)
}
}
Component {
id: collectiblesListView
ListDropdownContent {
isHeaderVisible: root.state === d.listView_depth1_State
headerModel: ListModel {
ListElement { key: "MINT"; icon: "add"; iconSize: 16; description: qsTr("Mint collectible"); rotation: 0; spacing: 8 }
}
model: d.currentModel
onHeaderItemClicked: {
if(key === "MINT") console.log("TODO: Mint collectible")
}
onItemClicked: {
if(subItems && root.state === d.listView_depth1_State) {
// One deep navigation
d.currentSubitems = subItems
d.currentItemName = name
d.currentItemSource = iconSource
root.state = d.listView_depth2_State
}
else {
d.reset()
root.itemClicked(key, name, iconSource)
}
}
}
}
Component {
id: thumbnailsView
ThumbnailsDropdownContent {
title: d.currentItemName
titleImage: d.currentItemSource
model: d.currentModel
onItemClicked: {
d.reset()
root.itemClicked(key, name, iconSource)
}
}
}
}

View File

@ -15,35 +15,55 @@ import utils 1.0
StatusDropdown {
id: root
property real tokenAmountValue: 0
property var store
property var itemKey
property real tokenAmount: 0
property string tokenName: d.defaultTokenNameText
property url tokenImage: ""
property real collectibleAmount: 1
property string collectibleName: d.defaultCollectibleNameText
property url collectibleImage: ""
property int operator: SQ.Utils.Operators.None
property bool withOperatorSelector: true
signal addToken(string tokenText, url tokenImage, int operator)
signal addItem(var itemKey, string itemText, url itemImage, int operator)
function reset() {
root.tokenAmountValue = 0
d.currentTabIndex = 0
root.itemKey = undefined
root.tokenAmount = 0
root.tokenName = d.defaultTokenNameText
root.tokenImage = ""
root.collectibleAmount = 1
root.collectibleName = d.defaultCollectibleNameText
root.collectibleImage = ""
root.operator = SQ.Utils.Operators.None
}
QtObject {
id: d
readonly property bool ready: root.tokenAmountValue > 0 && root.tokenName !== d.defaultTokenNameText
// Internal management properties:
readonly property bool tokensReady: root.tokenAmount > 0 && root.tokenName !== d.defaultTokenNameText
readonly property bool collectiblesReady: root.collectibleAmount > 0 && root.collectibleName !== d.defaultCollectibleNameText
readonly property string tokensState: "TOKENS"
readonly property string collectiblesState: "COLLECTIBLES"
readonly property string ensState: "ENS"
property bool isTokensLayout: true
property int currentTabIndex: 0
// By design values:
readonly property int initialHeight: 232
readonly property int mainHeight: 256
readonly property int mainExtendedHeight: 276
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")
property string defaultCollectibleNameText: qsTr("Choose collectible")
function selectInitState() {
if(root.withOperatorSelector)
@ -53,29 +73,21 @@ StatusDropdown {
}
}
width: d.defaultWidth
height: d.initialHeight
implicitWidth: d.defaultWidth
implicitHeight: loader.implicitHeight
contentItem: Loader {
id: loader
anchors.fill: parent
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
height: (item != null && typeof(item) !== 'undefined') ? item.implicitHeight : 0
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
if(sourceComponent == operatorsSelectorView) {
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
else {
root.width = d.defaultWidth
}
}
@ -85,123 +97,13 @@ StatusDropdown {
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
ColumnLayout {
StatusPickerButton {
Layout.fillWidth: true
Layout.margins: 8
Layout.bottomMargin: 0
Layout.preferredWidth: 143 // by design
Layout.preferredHeight: 36
horizontalPadding: 12
spacing: 10
@ -218,6 +120,8 @@ StatusDropdown {
}
}
StatusPickerButton {
Layout.margins: 8
Layout.topMargin: 0
Layout.fillWidth: true
Layout.preferredHeight: 36
horizontalPadding: 12
@ -238,40 +142,182 @@ StatusDropdown {
}
Component {
id: tokensExtendedView
id: tabsView
// 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 }
ColumnLayout {
spacing: 0
state: d.currentTabIndex === 0 ? d.tokensState : (d.currentTabIndex === 1 ? d.collectiblesState : d.ensState)
states: [
State {
name: d.tokensState
PropertyChanges {target: tabsLoader; sourceComponent: tokensCollectiblesLayout}
PropertyChanges {target: d; isTokensLayout: true}
},
State {
name: d.collectiblesState
PropertyChanges {target: tabsLoader; sourceComponent: tokensCollectiblesLayout}
PropertyChanges {target: d; isTokensLayout: false}
},
State {
name: d.ensState
PropertyChanges {target: tabsLoader; sourceComponent: ensLayout}
}
]
StatusIconTextButton {
visible: root.withOperatorSelector
Layout.leftMargin: 16
Layout.topMargin: 12
spacing: 0
statusIcon: "next"
icon.width: 12
icon.height: 12
iconRotation: 180
text: qsTr("Back")
onClicked: loader.sourceComponent = operatorsSelectorView
}
// 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"}
StatusSwitchTabBar {
id: tabBar
Layout.preferredWidth: 273 // by design
Layout.margins: 8
Layout.topMargin: root.withOperatorSelector ? 12 : 16
Layout.preferredHeight: 36 // by design
currentIndex: d.currentTabIndex
onCurrentIndexChanged: d.currentTabIndex = currentIndex
StatusSwitchTabButton {
text: qsTr("Token")
fontPixelSize: 13
}
StatusSwitchTabButton {
text: qsTr("Collectible")
fontPixelSize: 13
}
StatusSwitchTabButton {
text: qsTr("ENS")
fontPixelSize: 13
enabled: false // TODO
}
}
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")
Loader {
id: tabsLoader
Layout.fillWidth: true
Layout.margins: 8
}
}
}
Component {
id: tokensCollectiblesLayout
ColumnLayout {
spacing: 0
StatusPickerButton {
Layout.fillWidth: true
Layout.preferredHeight: 36
bgColor: Theme.palette.baseColor5
contentColor: Theme.palette.directColor1
text: d.isTokensLayout ? root.tokenName : root.collectibleName
font.pixelSize: 13
image.source: d.isTokensLayout ? root.tokenImage : root.collectibleImage
onClicked: loader.sourceComponent = extendedView
}
RowLayout {
visible: !d.isTokensLayout
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: 16
Layout.rightMargin: 6
Layout.topMargin: 8
StatusBaseText {
Layout.fillWidth: true
text: qsTr("Specific amount")
font.pixelSize: 13
wrapMode: Text.WordWrap
elide: Text.ElideRight
clip: true
}
StatusSwitch { id: specificAmountSwitch }
}
StatusInput {
Layout.fillWidth: true
Layout.topMargin: 8
visible: d.isTokensLayout ? true : specificAmountSwitch.checked
minimumHeight: 36
maximumHeight: 36
topPadding: 0
bottomPadding: 0
text: d.isTokensLayout ? (root.tokenAmount === 0 ? "" : root.tokenAmount.toString()) :
(root.collectibleAmount === 0 ? "" : root.collectibleAmount.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: {
if(d.isTokensLayout)
root.tokenAmount = Number(text)
else
root.collectibleAmount = Number(text)
}
}
StatusButton {
enabled: d.isTokensLayout ? d.tokensReady : d.collectiblesReady
text: qsTr("Add")
height: 44
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
Layout.topMargin: 32
onClicked: {
if(d.isTokensLayout)
root.addItem(root.itemKey, root.tokenAmount.toString() + " " + root.tokenName, root.tokenImage, root.operator)
else
root.addItem(root.itemKey, root.collectibleAmount.toString() + " " + root.collectibleName, root.collectibleImage, root.operator)
}
}
}
}
// TODO
Component {
id: ensLayout
Item {}
}
Component {
id: extendedView
ExtendedDropdownContent {
store: root.store
type: d.isTokensLayout ? ExtendedDropdownContent.Type.Tokens : ExtendedDropdownContent.Type.Collectibles
onGoBack: loader.sourceComponent = tabsView
onItemClicked: {
// Go back
loader.sourceComponent = tabsView
// Update new token info
root.tokenName = shortName
root.tokenImage = imageSource
if(d.isTokensLayout) {
// Update new token item info
root.tokenName = name
root.tokenImage = iconSource
}
else {
// Update new collectible item info
root.collectibleName = name
root.collectibleImage = iconSource
}
root.itemKey = key
}
}
}
}
}

View File

@ -13,19 +13,22 @@ StatusListView {
id: root
property var headerModel
property bool isHeaderVisible: true
property int maxHeight: 381 // default by design
signal headerItemClicked(int index)
signal itemClicked(string name, string shortName, url imageSource)
signal headerItemClicked(string key)
signal itemClicked(var key, string name, var shortName, url iconSource, var subItems)
implicitWidth: 273
implicitHeight: Math.min(contentHeight, root.maxHeight)
currentIndex: -1
clip: true
headerPositioning: ListView.OverlayHeader
header: Rectangle {
visible: root.isHeaderVisible
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
height: root.isHeaderVisible ? columnHeader.implicitHeight + 2 * columnHeader.anchors.topMargin : 0
ColumnLayout {
id: columnHeader
anchors.top: parent.top
@ -52,7 +55,7 @@ StatusListView {
}// End of Header
delegate: Rectangle {
width: ListView.view.width
height: 44 // by design
height: 45 // by design
color: mouseArea.containsMouse ? Theme.palette.baseColor4 : "transparent"
RowLayout {
anchors.fill: parent
@ -60,9 +63,9 @@ StatusListView {
spacing: 8
StatusRoundedImage {
Layout.alignment: Qt.AlignVCenter
image.source: model.imageSource
visible: model.imageSource.toString() !== ""
Layout.preferredWidth: 28
image.source: model.iconSource
visible: model.iconSource.toString() !== ""
Layout.preferredWidth: 32
Layout.preferredHeight: Layout.preferredWidth
}
ColumnLayout {
@ -78,21 +81,31 @@ StatusListView {
elide: Text.ElideRight
}
StatusBaseText {
visible: !!model.shortName
Layout.fillWidth: true
text: model.shortName
text: !!model.shortName ? model.shortName : ""
color: Theme.palette.baseColor1
font.pixelSize: 12
clip: true
elide: Text.ElideRight
}
}
StatusIcon {
icon: "tiny/chevron-right"
visible: !!model.subItems && model.subItems.count > 0
Layout.alignment: Qt.AlignVCenter
Layout.rightMargin: 16
color: Theme.palette.baseColor1
width: 16
height: 16
}
}
MouseArea {
id: mouseArea
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: { root.itemClicked(model.name, model.shortName, model.imageSource) }
onClicked: { root.itemClicked(model.key, model.name, model.shortName, model.iconSource, model.subItems) }
}
}// End of Item
section.property: "category"

View File

@ -0,0 +1,111 @@
import QtQuick 2.0
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.14
import StatusQ.Core.Theme 0.1
import StatusQ.Core 0.1
import StatusQ.Components 0.1
StatusScrollView {
id: root
property string title: ""
property url titleImage: ""
property string subtitle: ""
property ListModel model
property int maxHeight: 381 // default by design
signal itemClicked(var key, string name, url iconSource)
QtObject {
id: d
readonly property int imageSize: 133
readonly property int columns: 2
}
implicitHeight: Math.min(column.implicitHeight, root.maxHeight)
implicitWidth: d.imageSize * d.columns + grid.columnSpacing * (d.columns - 1)
clip: true
flickDeceleration: Flickable.VerticalFlick
ColumnLayout {
id: column
spacing: 4
Item {
Layout.fillWidth: true
height: 45 // by design
RowLayout {
anchors.fill: parent
anchors.leftMargin: 8
spacing: 8
StatusRoundedImage {
Layout.alignment: Qt.AlignVCenter
image.source: root.titleImage
visible: root.titleImage.toString() !== ""
Layout.preferredWidth: 32
Layout.preferredHeight: Layout.preferredWidth
}
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
spacing: 0
StatusBaseText {
Layout.fillWidth: true
text: root.title
color: Theme.palette.directColor1
font.pixelSize: 13
clip: true
elide: Text.ElideRight
}
StatusBaseText {
visible: root.subtitle
Layout.fillWidth: true
text: root.subtitle
color: Theme.palette.baseColor1
font.pixelSize: 12
clip: true
elide: Text.ElideRight
}
}
}
}
GridLayout {
id: grid
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
columnSpacing: 8
rowSpacing: 12
columns: d.columns
Repeater {
model: root.model
delegate: ColumnLayout {
spacing: 4
Rectangle {
Layout.preferredWidth: 133
Layout.preferredHeight: 133
color: "transparent"
Image {
source: model.imageSource
anchors.fill: parent
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: { root.itemClicked(model.key, model.name, model.iconSource) }
}
}
StatusBaseText {
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 8
text: model.name
color: Theme.palette.directColor1
font.pixelSize: 13
clip: true
elide: Text.ElideRight
}
}
}
}
}
}

View File

@ -45,6 +45,6 @@ SettingsPageLayout {
Component {
id: newPermissionView
CommunityNewPermissionView { }
CommunityNewPermissionView {}
}
}

View File

@ -0,0 +1,77 @@
import QtQuick 2.0
QtObject {
// TODO: Replace to real data, now dummy model
property var tokensModel: ListModel {
ListElement {key: "socks"; iconSource: "qrc:imports/assets/png/tokens/SOCKS.png"; name: "Unisocks"; shortName: "SOCKS"; category: "Community tokens"}
ListElement {key: "zrx"; iconSource: "qrc:imports/assets/png/tokens/ZRX.png"; name: "Ox"; shortName: "ZRX"; category: "Listed tokens"}
ListElement {key: "1inch"; iconSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "1inch"; shortName: "ZRX"; category: "Listed tokens"}
ListElement {key: "Aave"; iconSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "Aave"; shortName: "AAVE"; category: "Listed tokens"}
ListElement {key: "Amp"; iconSource: "qrc:imports/assets/png/tokens/CUSTOM-TOKEN.png"; name: "Amp"; shortName: "AMP"; category: "Listed tokens"}
}
// TODO: Replace to real data, now dummy model
property var collectiblesModel: ListModel {
ListElement {
key: "Anniversary"
iconSource: "qrc:imports/assets/png/collectibles/Anniversary.png"
name: "Anniversary"
category: "Community collectibles"
}
ListElement {
key: "CryptoKitties"
iconSource: "qrc:imports/assets/png/collectibles/CryptoKitties.png"
name: "CryptoKitties"
category: "Your collectibles"
subItems: [
ListElement {
key: "Kitty1"
iconSource: "qrc:imports/assets/png/collectibles/Furbeard.png"
imageSource: "qrc:imports/assets/png/collectibles/FurbeardBig.png"
name: "Furbeard"
},
ListElement {
key: "Kitty2"
iconSource: "qrc:imports/assets/png/collectibles/Magicat.png"
imageSource: "qrc:imports/assets/png/collectibles/MagicatBig.png"
name: "Magicat"
},
ListElement {
key: "Kitty3"
iconSource: "qrc:imports/assets/png/collectibles/HappyMeow.png"
imageSource: "qrc:imports/assets/png/collectibles/HappyMeowBig.png"
name: "Happy Meow"
},
ListElement {
key: "Kitty4"
iconSource: "qrc:imports/assets/png/collectibles/Furbeard.png"
imageSource: "qrc:imports/assets/png/collectibles/FurbeardBig.png"
name: "Furbeard-2"
},
ListElement {
key: "Kitty5"
iconSource: "qrc:imports/assets/png/collectibles/Magicat.png"
imageSource: "qrc:imports/assets/png/collectibles/MagicatBig.png"
name: "Magicat-3"
}
]
}
ListElement {
key: "SuperRare"
iconSource: "qrc:imports/assets/png/collectibles/SuperRare.png";
name: "SuperRare"
category: "Your collectibles"
}
ListElement {
key: "Custom"
iconSource: "qrc:imports/assets/png/collectibles/SNT.png"
name: "Custom Collectible"
category: "All collectibles"
}
}
function createPermissions(permissions) {
console.log("TODO: Create permissions - backend call")
}
}

View File

@ -10,15 +10,25 @@ import utils 1.0
import shared.panels 1.0
import "../../../Chat/controls/community"
import "../../stores"
Flickable {
id: root
property var store: CommunitiesStore {}
signal createPermission()
// TODO: Call this when permissions are stored in backend to start a new permissions flow
function clearPermissions() {
d.permissions.clear()
}
QtObject {
id: d
property bool isPrivate: false
property ListModel permissions: ListModel{}
}
contentWidth: mainLayout.width
@ -45,9 +55,11 @@ Flickable {
orOperatorText: qsTr("or")
popupItem: HoldingsDropdown {
id: dropdown
store: root.store
withOperatorSelector: tokensSelector.itemsModel.count > 0
onAddToken: {
tokensSelector.addItem(tokenText, tokenImage, operator)
onAddItem: {
tokensSelector.addItem(itemText, itemImage, operator)
d.permissions.append([{itemKey: itemKey}, {operator: operator}])
dropdown.close()
}
}
@ -117,11 +129,14 @@ Flickable {
StatusButton {
Layout.topMargin: 24
text: qsTr("Create permission")
enabled: false
enabled: d.permissions.count > 0
Layout.preferredHeight: 44
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
onClicked: root.createPermission()
onClicked: {
root.store.createPermissions(d.permissions)
root.clearPermissions()
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB