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
|
@ -1 +1 @@
|
|||
Subproject commit 01624316c2349f9179514c06a84e5e941823a2fc
|
||||
Subproject commit db0f8e9fb53863998403e3f6e7b3a2a62f173b4f
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -45,6 +45,6 @@ SettingsPageLayout {
|
|||
|
||||
Component {
|
||||
id: newPermissionView
|
||||
CommunityNewPermissionView { }
|
||||
CommunityNewPermissionView {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
After Width: | Height: | Size: 2.5 KiB |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 16 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 1.2 KiB |