feat(Community Permissions): Update `Who holds` tokens dropdown design and flow according to the new design

- Updated initial dropdown view for assets and collectibles to directly show the list of corresponding elements.
- Updated assets and collectibles panel when an item is selected, according to new design.
- Updated collectibles logic when amount is 1.
- Added collectibles group item as part of the selectable's options.
- Updated `storybook` according to new changes.
- Created reusable `TokenItem` component.
- Updated `CommunityPermissionsHelpers`.

Closes #9043
This commit is contained in:
Noelia 2023-01-18 20:54:14 +01:00 committed by Noelia
parent 66c62e472f
commit 3136ffb54d
17 changed files with 574 additions and 647 deletions

View File

@ -29,6 +29,15 @@
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2975%3A489607",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2975%3A492910"
],
"HoldingsDropdown": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22721%3A499660&t=F5yiYQV2YGPBdrJ8-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22721%3A499992&t=je3ZsbFwMntKAhKe-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22827%3A501381&t=7gqqAFbdG5KrPOmn-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22733%3A502088&t=5QSRGGAhrksqBs8e-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22772%3A496580&t=7gqqAFbdG5KrPOmn-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22772%3A496653&t=7gqqAFbdG5KrPOmn-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22734%3A502737&t=7gqqAFbdG5KrPOmn-0"
],
"InDropdown": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2934%3A482182",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2934%3A482231",
@ -65,12 +74,12 @@
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=8159%3A416160"
],
"CommunityPermissionsSettingsPanel": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2992%3A367890&t=IfHHp5GSPo1j9m5j-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2934%3A480020&t=IfHHp5GSPo1j9m5j-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2934%3A484715&t=IfHHp5GSPo1j9m5j-0"
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2992%3A367890&t=7gqqAFbdG5KrPOmn-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22137%3A484809&t=7gqqAFbdG5KrPOmn-0",
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22813%3A497277&t=7gqqAFbdG5KrPOmn-0"
],
"CommunityPermissionsView": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2934%3A484715&t=IfHHp5GSPo1j9m5j-0"
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=22813%3A497277&t=7gqqAFbdG5KrPOmn-0"
],
"PermissionQualificationPanel": [
"https://www.figma.com/file/17fc13UBFvInrLgNUKJJg5/Kuba%E2%8E%9CDesktop?node-id=2934%3A480089",

View File

@ -9,11 +9,6 @@ import AppLayouts.Chat.controls.community 1.0
Pane {
id: root
function openFlow(flowType) {
holdingsDropdown.close()
holdingsDropdown.openFlow(flowType)
}
RowLayout {
Label {
text: "Open flow:"
@ -21,12 +16,19 @@ Pane {
Button {
text: "Add"
onClicked: openFlow(HoldingsDropdown.FlowType.Add)
onClicked: {
holdingsDropdown.close()
holdingsDropdown.open()
}
}
Button {
text: "Update"
onClicked: openFlow(HoldingsDropdown.FlowType.Update)
onClicked: {
holdingsDropdown.close()
holdingsDropdown.setActiveTab(HoldingTypes.Type.Ens)
holdingsDropdown.openUpdateFlow()
}
}
}
@ -88,6 +90,9 @@ Pane {
}
onOpened: contentItem.parent.parent = root
Component.onCompleted: openFlow(HoldingsDropdown.FlowType.Add)
Component.onCompleted: {
holdingsDropdown.close()
holdingsDropdown.open()
}
}
}

View File

@ -1,47 +0,0 @@
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 shared.controls 1.0
ColumnLayout {
id: root
property alias assetName: pickerButton.text
property url assetImage
property alias amountText: amountInput.text
property alias amount: amountInput.amount
readonly property bool amountValid: amountInput.valid && amountInput.text.length > 0
signal pickerClicked
function setAmount(amount) {
amountInput.setAmount(amount)
}
spacing: 0
StatusPickerButton {
id: pickerButton
Layout.fillWidth: true
Layout.preferredHeight: 36
bgColor: Theme.palette.baseColor5
contentColor: Theme.palette.directColor1
font.pixelSize: 13
asset.name: root.assetImage
onClicked: pickerClicked()
}
AmountInput {
id: amountInput
Layout.fillWidth: true
Layout.topMargin: 8
}
}

View File

@ -1,70 +0,0 @@
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 shared.controls 1.0
ColumnLayout {
id: root
property alias specificAmount: specificAmountSwitch.checked
property alias collectibleName: pickerButton.text
property url collectibleImage
property alias amountText: amountInput.text
property alias amount: amountInput.amount
readonly property bool amountValid: amountInput.valid && amountInput.text.length > 0
signal pickerClicked
function setAmount(amount) {
amountInput.setAmount(amount)
}
spacing: 0
StatusPickerButton {
id: pickerButton
Layout.fillWidth: true
Layout.preferredHeight: 36
bgColor: Theme.palette.baseColor5
contentColor: Theme.palette.directColor1
text: root.collectibleName
font.pixelSize: 13
asset.name: root.collectibleImage
onClicked: pickerClicked()
}
RowLayout {
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
}
StatusSwitch { id: specificAmountSwitch }
}
AmountInput {
id: amountInput
visible: specificAmountSwitch.checked
Layout.fillWidth: true
Layout.topMargin: 8
allowDecimals: false
}
}

View File

@ -6,10 +6,17 @@ import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Controls.Validators 0.1
ColumnLayout {
id: root
property int mode: HoldingTypes.Mode.Add
property alias domainName: domainNameInput.text
property alias domainNameValid: domainNameInput.valid
property alias addButtonEnabled: addOrUpdateButton.enabled
signal addClicked
signal updateClicked
signal removeClicked
spacing: 0
@ -56,4 +63,26 @@ ColumnLayout {
lineHeight: 18
lineHeightMode: Text.FixedHeight
}
StatusButton {
id: addOrUpdateButton
text: (root.mode === HoldingTypes.Mode.Add) ? qsTr("Add") : qsTr("Update")
Layout.topMargin: 40
Layout.preferredHeight: 44 // by design
Layout.fillWidth: true
onClicked: root.mode === HoldingTypes.Mode.Add
? root.addClicked() : root.updateClicked()
}
StatusFlatButton {
text: qsTr("Remove")
Layout.topMargin: 16 // by design
Layout.preferredHeight: 44 // by design
Layout.fillWidth: true
visible: root.mode === HoldingTypes.Mode.Update
type: StatusBaseButton.Type.Danger
onClicked: root.removeClicked()
}
}

View File

@ -21,6 +21,7 @@ Item {
readonly property bool canGoBack: root.state !== d.listView_depth1_State
signal itemClicked(string key, string name, url iconSource)
signal navigateDeep(string key, var subItems)
enum Type{
Assets,
@ -31,6 +32,14 @@ Item {
root.state = d.listView_depth1_State
}
function goForward(key, itemName, itemSource, subItems) {
d.currentSubitems = subItems
d.currentItemKey = key
d.currentItemName = itemName
d.currentItemSource = itemSource
root.state = d.listView_depth2_State
}
QtObject {
id: d
readonly property int filterItemsHeight: 36
@ -44,6 +53,7 @@ Item {
readonly property string listView_depth2_State: "LIST-DEPTH2"
property var currentModel: root.store.collectiblesModel
property var currentSubitems
property string currentItemKey: ""
property string currentItemName: ""
property url currentItemSource: ""
@ -61,6 +71,7 @@ Item {
}
function reset() {
d.currentItemKey = ""
d.currentItemName = ""
d.currentItemSource = ""
d.currentModel = root.store.collectiblesModel
@ -85,6 +96,10 @@ Item {
? root.store.assetsModel : collectiblesFilteredModel//root.store.collectiblesModel
isFilterOptionVisible: false
}
PropertyChanges {
target: tokenGroupItem
visible: false
}
PropertyChanges {
target: searcher
visible: type === ExtendedDropdownContent.Type.Collectibles
@ -102,6 +117,10 @@ Item {
currentModel: d.currentSubitems
isFilterOptionVisible: true
}
PropertyChanges {
target: tokenGroupItem
visible: true
}
},
State {
name: d.thumbnailsViewState
@ -115,6 +134,10 @@ Item {
currentModel: d.currentSubitems
isFilterOptionVisible: true
}
PropertyChanges {
target: tokenGroupItem
visible: true
}
}
]
@ -247,6 +270,18 @@ Item {
maximumHeight: 36
}
TokenItem {
id: tokenGroupItem
Layout.fillWidth: true
key: d.currentItemKey
name: d.currentItemName
iconSource: d.currentItemSource
enabled: true
onItemClicked: root.itemClicked(d.currentItemKey,
d.currentItemName,
d.currentItemSource)
}
Loader {
id: contentLoader
@ -292,9 +327,11 @@ Item {
if(subItems && root.state === d.listView_depth1_State) {
// One deep navigation
d.currentSubitems = subItems
d.currentItemKey = key
d.currentItemName = name
d.currentItemSource = iconSource
root.state = d.listView_depth2_State
root.navigateDeep(key, subItems)
}
else {
d.reset()

View File

@ -4,4 +4,8 @@ QtObject {
enum Type {
Asset, Collectible, Ens
}
enum Mode {
Add, Update
}
}

View File

@ -9,7 +9,6 @@ import StatusQ.Core.Utils 0.1
import AppLayouts.Chat.helpers 1.0
StatusDropdown {
id: root
@ -20,7 +19,6 @@ StatusDropdown {
property string collectibleKey: ""
property real collectibleAmount: 1
property bool collectiblesSpecificAmount: false
property string ensDomainName: ""
@ -34,46 +32,18 @@ StatusDropdown {
signal removeClicked
function reset() {
d.currentHoldingType = HoldingTypes.Type.Asset
d.assetAmountText = ""
d.collectibleAmountText = ""
root.assetKey = ""
root.collectibleKey = ""
root.assetAmount = 0
root.collectibleAmount = 1
root.collectiblesSpecificAmount = false
root.ensDomainName = ""
statesStack.clear()
}
width: d.defaultWidth
padding: d.padding
// force keeping within the bounds of the enclosing window
margins: 0
onClosed: root.reset()
enum FlowType {
Add, Update
Selected, List_Deep1, List_Deep2
}
function openFlow(flowType) {
switch (flowType) {
case HoldingsDropdown.FlowType.Add:
statesStack.push(d.addState)
break
case HoldingsDropdown.FlowType.Update:
statesStack.push(d.updateState)
break
default:
console.warn("Unknown flow type.")
return
}
function openUpdateFlow() {
d.currentHoldingMode = HoldingTypes.Mode.Update
if(d.currentHoldingType !== HoldingTypes.Type.Ens) {
if(statesStack.size === 0)
statesStack.push(HoldingsDropdown.FlowType.List_Deep1)
statesStack.push(HoldingsDropdown.FlowType.Selected)
}
open()
}
@ -81,50 +51,60 @@ StatusDropdown {
d.currentHoldingType = holdingType
}
function reset() {
d.currentHoldingType = HoldingTypes.Type.Asset
d.currentHoldingMode = HoldingTypes.Mode.Add
d.assetAmountText = ""
d.collectibleAmountText = ""
root.assetKey = ""
root.collectibleKey = ""
root.assetAmount = 0
root.collectibleAmount = 1
root.ensDomainName = ""
d.setInitialFlow()
}
QtObject {
id: d
// Internal management properties and signals:
readonly property var holdingTypes: [
HoldingTypes.Type.Asset, HoldingTypes.Type.Collectible, HoldingTypes.Type.Ens
]
readonly property bool assetsReady: root.assetAmount > 0 && root.assetKey
readonly property bool collectiblesReady: root.collectibleAmount > 0 && root.collectibleKey
readonly property bool ensReady: d.ensDomainNameValid
readonly property string addState: "ADD"
readonly property string updateState: "UPDATE"
readonly property string extendedState: "EXTENDED"
property int holdingsTabMode: HoldingsTabs.Mode.Add
property int extendedDropdownType: ExtendedDropdownContent.Type.Assets
property int currentHoldingType: HoldingTypes.Type.Asset
property int currentHoldingMode: HoldingTypes.Mode.Add
property bool extendedDeepNavigation: false
property var currentSubItems
property string currentItemKey: ""
property string assetAmountText: ""
property string collectibleAmountText: ""
property int currentHoldingType: HoldingTypes.Type.Asset
property string collectibleAmountText: "1"
property bool ensDomainNameValid: false
signal addClicked
signal updateClicked
// By design values:
readonly property int padding: 8
readonly property int defaultWidth: 289
readonly property int extendedContentHeight: 380
readonly property int tabsAddModeBaseHeight: 232 - padding * 2
readonly property int tabsAddModeExtendedHeight: 277 - padding * 2
readonly property int tabsUpdateModeBaseHeight: 284 - padding * 2
readonly property int tabsUpdateModeExtendedHeight: tabsUpdateModeBaseHeight
+ (tabsAddModeExtendedHeight - tabsAddModeBaseHeight)
readonly property int tabBarHeigh: 36
readonly property int tabBarTextSize: 13
readonly property int backButtonWidth: 56
readonly property int backButtonHeight: 24
readonly property int backButtonToContentSpace: 8
readonly property string defaultAssetNameText: qsTr("Choose asset")
readonly property string defaultCollectibleNameText: qsTr("Choose collectible")
function setInitialFlow() {
statesStack.clear()
if(d.currentHoldingType !== HoldingTypes.Type.Ens)
statesStack.push(HoldingsDropdown.FlowType.List_Deep1)
else
statesStack.push(HoldingsDropdown.FlowType.Selected)
}
}
QtObject {
@ -153,6 +133,9 @@ StatusDropdown {
}
}
width: d.defaultWidth
padding: d.padding
margins: 0 // force keeping within the bounds of the enclosing window
contentItem: ColumnLayout {
id: content
@ -163,9 +146,7 @@ StatusDropdown {
Layout.preferredWidth: d.backButtonWidth
Layout.preferredHeight: d.backButtonHeight
visible: statesStack.size > 1
spacing: 0
leftPadding: 4
statusIcon: "next"
@ -175,6 +156,50 @@ StatusDropdown {
text: qsTr("Back")
}
StatusSwitchTabBar {
id: tabBar
visible: !backButton.visible
Layout.preferredHeight: d.tabBarHeigh
Layout.fillWidth: true
currentIndex: d.holdingTypes.indexOf(d.currentHoldingType)
state: d.currentHoldingType
states: [
State {
name: HoldingTypes.Type.Asset
PropertyChanges {target: loader; sourceComponent: listLayout}
PropertyChanges {target: root; height: d.extendedContentHeight}
PropertyChanges {target: d; extendedDropdownType: ExtendedDropdownContent.Type.Assets}
},
State {
name: HoldingTypes.Type.Collectible
PropertyChanges {target: loader; sourceComponent: listLayout}
PropertyChanges {target: root; height: d.extendedContentHeight}
PropertyChanges {target: d; extendedDropdownType: ExtendedDropdownContent.Type.Collectibles}
},
State {
name: HoldingTypes.Type.Ens
PropertyChanges {target: loader; sourceComponent: ensLayout}
PropertyChanges {target: root; height: undefined} // use implicit height
}
]
onCurrentIndexChanged: {
d.currentHoldingType = d.holdingTypes[currentIndex]
d.setInitialFlow()
}
Repeater {
id: tabLabelsRepeater
model: [qsTr("Asset"), qsTr("Collectible"), qsTr("ENS")]
StatusSwitchTabButton {
text: modelData
fontPixelSize: d.tabBarTextSize
}
}
}
Loader {
id: loader
Layout.fillWidth: true
@ -183,185 +208,144 @@ StatusDropdown {
states: [
State {
name: d.addState
PropertyChanges {target: loader; sourceComponent: tabsView}
name: HoldingsDropdown.FlowType.Selected
PropertyChanges {target: loader; sourceComponent: (d.currentHoldingType === HoldingTypes.Type.Asset) ? assetLayout :
((d.currentHoldingType === HoldingTypes.Type.Collectible) ? collectibleLayout : ensLayout) }
PropertyChanges {target: root; height: undefined} // use implicit height
},
State {
name: d.updateState
extend: d.addState
PropertyChanges {target: d; holdingsTabMode: HoldingsTabs.Mode.Update}
name: HoldingsDropdown.FlowType.List_Deep1
PropertyChanges {target: loader; sourceComponent: listLayout}
PropertyChanges {target: root; height: d.extendedContentHeight}
PropertyChanges {target: d; extendedDeepNavigation: false}
},
State {
name: d.extendedState
PropertyChanges {target: loader; sourceComponent: extendedView}
PropertyChanges {target: root; height: d.extendedContentHeight}
name: HoldingsDropdown.FlowType.List_Deep2
extend: HoldingsDropdown.FlowType.List_Deep1
PropertyChanges {target: d; extendedDeepNavigation: true}
}
]
}
onClosed: root.reset()
Component {
id: tabsView
id: listLayout
HoldingsTabs {
id: holdingsTabs
ExtendedDropdownContent {
id: listPanel
readonly property var holdingTypes: [
HoldingTypes.Type.Asset, HoldingTypes.Type.Collectible, HoldingTypes.Type.Ens
]
readonly property var labels: [qsTr("Asset"), qsTr("Collectible"), qsTr("ENS")]
store: root.store
type: d.extendedDropdownType
readonly property bool extendedHeight:
d.currentHoldingType === HoldingTypes.Type.Collectible && collectiblesSpecificAmount ||
d.currentHoldingType === HoldingTypes.Type.Ens
onItemClicked: {
if(d.extendedDropdownType === ExtendedDropdownContent.Type.Assets)
root.assetKey = key
else
root.collectibleKey = key
implicitHeight: extendedHeight
? (mode === HoldingsTabs.Mode.Add ? d.tabsAddModeExtendedHeight : d.tabsUpdateModeExtendedHeight)
: (mode === HoldingsTabs.Mode.Add ? d.tabsAddModeBaseHeight : d.tabsUpdateModeBaseHeight)
statesStack.push(HoldingsDropdown.FlowType.Selected)
}
states: [
State {
name: HoldingTypes.Type.Asset
PropertyChanges {target: holdingsTabs; sourceComponent: assetsLayout; addOrUpdateButtonEnabled: d.assetsReady}
},
State {
name: HoldingTypes.Type.Collectible
PropertyChanges {target: holdingsTabs; sourceComponent: collectiblesLayout; addOrUpdateButtonEnabled: d.collectiblesReady}
},
State {
name: HoldingTypes.Type.Ens
PropertyChanges {target: holdingsTabs; sourceComponent: ensLayout; addOrUpdateButtonEnabled: d.ensReady}
}
]
onNavigateDeep: {
d.currentSubItems = subItems
d.currentItemKey = key
statesStack.push(HoldingsDropdown.FlowType.List_Deep2)
}
tabLabels: labels
state: d.currentHoldingType
mode: d.holdingsTabMode
currentIndex: holdingTypes.indexOf(d.currentHoldingType)
onCurrentIndexChanged: d.currentHoldingType = holdingTypes[currentIndex]
onAddClicked: d.addClicked()
onUpdateClicked: d.updateClicked()
onRemoveClicked: root.removeClicked()
Component.onCompleted: {
if(d.extendedDeepNavigation)
listPanel.goForward(d.currentItemKey,
CommunityPermissionsHelpers.getTokenNameByKey(store.collectiblesModel, d.currentItemKey),
CommunityPermissionsHelpers.getTokenIconByKey(store.collectiblesModel, d.currentItemKey),
d.currentSubItems)
}
Connections {
target: backButton
function onClicked() {
if (listPanel.canGoBack)
listPanel.goBack()
statesStack.pop()
}
}
Connections {
target: root
function onClosed() { listPanel.goBack() }
}
}
}
Component {
id: assetsLayout
id: assetLayout
AssetsPanel {
id: assetsPanel
assetName: d.defaultAssetNameText
amountText: d.assetAmountText
onAmountTextChanged: d.assetAmountText = amountText
TokenPanel {
id: assetPanel
readonly property real effectiveAmount: amountValid ? amount : 0
tokenName: CommunityPermissionsHelpers.getTokenNameByKey(store.assetsModel, root.assetKey)
tokenShortName: CommunityPermissionsHelpers.getTokenShortNameByKey(store.assetsModel, root.assetKey)
tokenImage: CommunityPermissionsHelpers.getTokenIconByKey(store.assetsModel, root.assetKey)
amountText: d.assetAmountText
tokenCategoryText: qsTr("Asset")
addOrUpdateButtonEnabled: d.assetsReady
mode: d.currentHoldingMode
onEffectiveAmountChanged: root.assetAmount = effectiveAmount
onPickerClicked: {
d.extendedDropdownType = ExtendedDropdownContent.Type.Assets
statesStack.push(d.extendedState)
}
readonly property string assetKey: root.assetKey
onAssetKeyChanged: {
const modelItem = CommunityPermissionsHelpers.getAssetByKey(
store.assetsModel, assetKey)
if (modelItem) {
assetsPanel.assetName = modelItem.shortName
assetsPanel.assetImage = modelItem.iconSource
} else {
assetsPanel.assetName = d.defaultAssetNameText
assetsPanel.assetImage = ""
}
}
onAmountTextChanged: d.assetAmountText = amountText
onAddClicked: root.addAsset(root.assetKey, root.assetAmount)
onUpdateClicked: root.updateAsset(root.assetKey, root.assetAmount)
onRemoveClicked: root.removeClicked()
Component.onCompleted: {
if (d.assetAmountText.length === 0 && root.assetAmount)
assetsPanel.setAmount(root.assetAmount)
assetPanel.setAmount(root.assetAmount)
}
Connections {
target: d
target: backButton
function onAddClicked() {
root.addAsset(root.assetKey, root.assetAmount)
}
function onUpdateClicked() {
root.updateAsset(root.assetKey, root.assetAmount)
}
function onClicked() { statesStack.pop() }
}
}
}
Component {
id: collectiblesLayout
id: collectibleLayout
CollectiblesPanel {
id: collectiblesPanel
collectibleName: d.defaultCollectibleNameText
amountText: d.collectibleAmountText
onAmountTextChanged: d.collectibleAmountText = amountText
TokenPanel {
id: collectiblePanel
readonly property real effectiveAmount: amountValid ? amount : 0
tokenName: CommunityPermissionsHelpers.getTokenNameByKey(store.collectiblesModel, root.collectibleKey)
tokenShortName: ""
tokenImage: CommunityPermissionsHelpers.getTokenIconByKey(store.collectiblesModel, root.collectibleKey)
amountText: d.collectibleAmountText
tokenCategoryText: qsTr("Collectible")
addOrUpdateButtonEnabled: d.collectiblesReady
allowDecimals: false
mode: d.currentHoldingMode
onEffectiveAmountChanged: root.collectibleAmount = effectiveAmount
specificAmount: root.collectiblesSpecificAmount
onSpecificAmountChanged: root.collectiblesSpecificAmount = specificAmount
onPickerClicked: {
d.extendedDropdownType = ExtendedDropdownContent.Type.Collectibles
statesStack.push(d.extendedState)
}
onAmountTextChanged: d.collectibleAmountText = amountText
onAddClicked: root.addCollectible(root.collectibleKey, root.collectibleAmount)
onUpdateClicked: root.updateCollectible(root.collectibleKey, root.collectibleAmount)
onRemoveClicked: root.removeClicked()
Component.onCompleted: {
if (d.collectibleAmountText.length === 0 && root.collectibleAmount)
collectiblesPanel.setAmount(root.collectibleAmount)
}
function getAmount() {
return specificAmount ? effectiveAmount : 1
if (d.collectibleAmountText.length === 0 && root.collectibleAmount)
collectiblePanel.setAmount(root.collectibleAmount)
}
Connections {
target: d
target: backButton
function onAddClicked() {
root.addCollectible(root.collectibleKey, collectiblesPanel.getAmount())
}
function onUpdateClicked() {
root.updateCollectible(root.collectibleKey, collectiblesPanel.getAmount())
}
}
readonly property string collectibleKey: root.collectibleKey
onCollectibleKeyChanged: {
const modelItem = CommunityPermissionsHelpers.getCollectibleByKey(
store.collectiblesModel, collectibleKey)
if (modelItem) {
collectiblesPanel.collectibleName = modelItem.name
collectiblesPanel.collectibleImage = modelItem.iconSource
} else {
collectiblesPanel.collectibleName = d.defaultCollectibleNameText
collectiblesPanel.collectibleImage = ""
}
function onClicked() { statesStack.pop() }
}
}
}
@ -370,52 +354,15 @@ StatusDropdown {
id: ensLayout
EnsPanel {
addButtonEnabled: d.ensReady
domainName: root.ensDomainName
mode: d.currentHoldingMode
onDomainNameChanged: root.ensDomainName = domainName
onDomainNameValidChanged: d.ensDomainNameValid = domainNameValid
Connections {
target: d
function onAddClicked() {
root.addEns(root.ensDomainName)
}
function onUpdateClicked() {
root.updateEns(root.ensDomainName)
}
}
}
}
Component {
id: extendedView
ExtendedDropdownContent {
id: extendedDropdown
store: root.store
type: d.extendedDropdownType
onItemClicked: {
statesStack.pop()
if(d.extendedDropdownType === ExtendedDropdownContent.Type.Assets)
root.assetKey = key
else
root.collectibleKey = key
}
Connections {
target: backButton
function onClicked() {
if (extendedDropdown.canGoBack)
extendedDropdown.goBack()
else
statesStack.pop()
}
}
onAddClicked: root.addEns(root.ensDomainName)
onUpdateClicked: root.updateEns(root.ensDomainName)
onRemoveClicked: root.removeClicked()
}
}
}

View File

@ -1,95 +0,0 @@
import QtQuick 2.14
import StatusQ.Controls 0.1
Item {
id: root
property int mode: HoldingsTabs.Mode.Add
property alias tabLabels: tabLabelsRepeater.model
property alias sourceComponent: tabsLoader.sourceComponent
property alias addOrUpdateButtonEnabled: addOrUpdateButton.enabled
property alias currentIndex: tabBar.currentIndex
readonly property alias item: tabsLoader.item
signal addClicked
signal updateClicked
signal removeClicked
enum Mode {
Add, Update
}
function setCurrentIndex(index) {
tabBar.setCurrentIndex(index)
}
QtObject {
id: d
// values from design
readonly property int tabBarHeight: 36
readonly property int tabBarFontPixelSize: 13
readonly property int contentTopMargin: 16
readonly property int buttonsHeight: 44
readonly property int buttonsSpacing: 8
}
StatusSwitchTabBar {
id: tabBar
anchors.top: parent.top
anchors.right: parent.right
anchors.left: parent.left
height: d.tabBarHeight
Repeater {
id: tabLabelsRepeater
StatusSwitchTabButton {
text: modelData
fontPixelSize: d.tabBarFontPixelSize
}
}
}
Loader {
id: tabsLoader
anchors.top: tabBar.bottom
anchors.right: parent.right
anchors.left: parent.left
anchors.topMargin: d.contentTopMargin
}
Column {
spacing: d.buttonsSpacing
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.left: parent.left
StatusButton {
id: addOrUpdateButton
text: (root.mode === HoldingsTabs.Mode.Add ? qsTr("Add") : qsTr("Update"))
height: d.buttonsHeight
width: parent.width
onClicked: root.mode === HoldingsTabs.Mode.Add
? root.addClicked() : root.updateClicked()
}
StatusFlatButton {
text: qsTr("Remove")
height: d.buttonsHeight
width: parent.width
visible: root.mode === HoldingsTabs.Mode.Update
type: StatusBaseButton.Type.Danger
onClicked: root.removeClicked()
}
}
}

View File

@ -53,61 +53,20 @@ StatusListView {
}
}
}// End of Header
delegate: Rectangle {
delegate: TokenItem {
width: ListView.view.width
height: 45 // by design
color: mouseArea.containsMouse ? Theme.palette.baseColor4 : "transparent"
RowLayout {
anchors.fill: parent
anchors.leftMargin: 6
spacing: 8
StatusRoundedImage {
Layout.alignment: Qt.AlignVCenter
image.source: model.iconSource
visible: model.iconSource.toString() !== ""
Layout.preferredWidth: 32
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 {
visible: !!model.shortName
Layout.fillWidth: true
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.key, model.name, model.shortName, model.iconSource, model.subItems) }
}
}// End of Item
key: model.key
name: model.name
shortName: !!model.shortName ? model.shortName : ""
iconSource: model.iconSource
subItems: model.subItems
enabled: true
onItemClicked: root.itemClicked(model.key,
model.name,
model.shortName,
model.iconSource,
model.subItems)
}
section.property: "category"
section.criteria: ViewSection.FullString
section.delegate: Item {

View File

@ -23,87 +23,45 @@ StatusScrollView {
readonly property int columns: 2
}
implicitHeight: Math.min(column.implicitHeight, root.maxHeight)
implicitHeight: Math.min(grid.implicitHeight, root.maxHeight)
implicitWidth: d.imageSize * d.columns + grid.columnSpacing * (d.columns - 1)
clip: true
flickDeceleration: Flickable.VerticalFlick
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
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
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 ? model.imageSource : ""
anchors.fill: parent
}
StatusBaseText {
visible: root.subtitle
Layout.fillWidth: true
text: root.subtitle
color: Theme.palette.baseColor1
font.pixelSize: 12
clip: true
elide: Text.ElideRight
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
hoverEnabled: true
onClicked: { root.itemClicked(model.key, model.name, model.iconSource) }
}
}
}
}
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
}
StatusBaseText {
Layout.alignment: Qt.AlignLeft
Layout.leftMargin: 8
text: model.name
color: Theme.palette.directColor1
font.pixelSize: 13
elide: Text.ElideRight
}
}
}

View File

@ -0,0 +1,83 @@
import QtQuick 2.13
import QtQuick.Layouts 1.14
import QtQuick.Controls 2.12
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
Control {
id: root
property string key
property string name
property string shortName
property url iconSource
property var subItems
signal itemClicked(string key, string name, string shortName, url iconSource, var subItems)
leftPadding: 6 // by design
implicitHeight: 45 // by design
spacing: 8 // by design
background: Rectangle {
color: mouseArea.containsMouse ? Theme.palette.baseColor4 : "transparent"
MouseArea {
id: mouseArea
anchors.fill: parent
cursorShape: root.enabled ? Qt.PointingHandCursor : Qt.ArrowCursor
hoverEnabled: true
onClicked: root.itemClicked(root.key,
root.name,
root.shortName,
root.iconSource,
root.subItems)
}
}
contentItem: RowLayout {
spacing: root.spacing
StatusRoundedImage {
Layout.alignment: Qt.AlignVCenter
image.source: root.iconSource
visible: root.iconSource.toString() !== ""
Layout.preferredWidth: 32
Layout.preferredHeight: Layout.preferredWidth
}
ColumnLayout {
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
spacing: 0
StatusBaseText {
Layout.fillWidth: true
text: root.name
color: Theme.palette.directColor1
font.pixelSize: 13
elide: Text.ElideRight
}
StatusBaseText {
visible: !!root.shortName
Layout.fillWidth: true
text: !!root.shortName ? root.shortName : ""
color: Theme.palette.baseColor1
font.pixelSize: 12
elide: Text.ElideRight
}
}
StatusIcon {
icon: "tiny/chevron-right"
visible: !!root.subItems && root.subItems.count > 0
Layout.alignment: Qt.AlignVCenter
Layout.rightMargin: 16
color: Theme.palette.baseColor1
width: 16
height: 16
}
}
}

View File

@ -0,0 +1,91 @@
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 StatusQ.Components 0.1
import shared.controls 1.0
ColumnLayout {
id: root
property int mode: HoldingTypes.Mode.Add
property alias tokenName: item.name
property alias tokenShortName: item.shortName
property alias tokenImage: item.iconSource
property alias amountText: amountInput.text
property alias amount: amountInput.amount
property alias tokenCategoryText: tokenLabel.text
property alias addOrUpdateButtonEnabled: addOrUpdateButton.enabled
property alias allowDecimals: amountInput.allowDecimals
readonly property bool amountValid: amountInput.valid && amountInput.text.length > 0
signal addClicked
signal updateClicked
signal removeClicked
function setAmount(amount) {
amountInput.setAmount(amount)
}
QtObject {
id: d
// values from design
readonly property int defaultHeight: 44
readonly property int defaultSpacing: 8
}
spacing: d.defaultSpacing
StatusBaseText {
id: tokenLabel
Layout.topMargin: 2 * d.defaultSpacing
Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter
Layout.leftMargin: d.defaultSpacing
color: Theme.palette.baseColor1
font.pixelSize: 12
elide: Text.ElideRight
}
TokenItem {
id: item
Layout.fillWidth: true
enabled: false
}
AmountInput {
id: amountInput
Layout.fillWidth: true
Layout.bottomMargin: (validationError !== "") ? root.spacing : 0
customHeight: d.defaultHeight
allowDecimals: true
keepHeight: true
}
StatusButton {
id: addOrUpdateButton
text: (root.mode === HoldingTypes.Mode.Add) ? qsTr("Add") : qsTr("Update")
Layout.preferredHeight: d.defaultHeight
Layout.topMargin: d.defaultSpacing
Layout.fillWidth: true
onClicked: root.mode === HoldingTypes.Mode.Add
? root.addClicked() : root.updateClicked()
}
StatusFlatButton {
text: qsTr("Remove")
Layout.preferredHeight: d.defaultHeight
Layout.fillWidth: true
Layout.topMargin: d.defaultSpacing
visible: root.mode === HoldingTypes.Mode.Update
type: StatusBaseButton.Type.Danger
onClicked: root.removeClicked()
}
}

View File

@ -2,38 +2,70 @@ pragma Singleton
import QtQml 2.14
import StatusQ.Core 0.1
import utils 1.0
import AppLayouts.Chat.controls.community 1.0
QtObject {
readonly property QtObject _d: QtObject {
id: d
function getByKey(model, key) {
for (let i = 0; i < model.count; i++) {
const item = model.get(i)
if (item.key === key)
return item
}
function getTokenByKey(model, key) {
if (!model)
return null
}
}
function getAssetByKey(assetsModel, key) {
return d.getByKey(assetsModel, key)
}
function getCollectibleByKey(collectiblesModel, key) {
for (let i = 0; i < collectiblesModel.count; i++) {
const item = collectiblesModel.get(i)
if (!!item.subItems) {
const sub = d.getByKey(item.subItems, key)
if (sub)
return sub
} else if (item.key === key) {
for (let i = 0; i < model.count; i++) {
const item = model.get(i)
if (item.key === key)
return item
if (item.subItems) {
const subitem = getTokenByKey(item.subItems, key)
if (subitem !== null)
return subitem
}
}
return null
}
function getTokenNameByKey(model, key) {
const item = getTokenByKey(model, key)
if (item)
return item.name
return ""
}
function getTokenShortNameByKey(model, key) {
const item = getTokenByKey(model, key)
if (item)
return item.shortName
return ""
}
function getTokenIconByKey(model, key) {
const item = getTokenByKey(model, key)
if (item)
return item.iconSource
return ""
}
function setHoldingsTextFormat(type, name, amount) {
switch (type) {
case HoldingTypes.Type.Asset:
return `${LocaleUtils.numberToLocaleString(amount)} ${name}`
case HoldingTypes.Type.Collectible:
if (amount === 1)
return name
return `${LocaleUtils.numberToLocaleString(amount)} ${name}`
case HoldingTypes.Type.Ens:
if (name === "*.eth")
return qsTr("Any ENS username")
if (name.startsWith("*."))
return qsTr("ENS username on '%1' domain").arg(name.substring(2))
return qsTr("ENS username '%1'").arg(name)
default:
return ""
}
}
}

View File

@ -1,10 +1,5 @@
import QtQuick 2.0
import AppLayouts.Chat.controls.community 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Utils 0.1 as SQ
import utils 1.0
QtObject {
id: root
@ -134,23 +129,6 @@ QtObject {
}
}
function setHoldingsTextFormat(type, name, amount) {
switch (type) {
case HoldingTypes.Type.Asset:
case HoldingTypes.Type.Collectible:
return `${LocaleUtils.numberToLocaleString(amount)} ${name}`
case HoldingTypes.Type.Ens:
if (name === "*.eth")
return qsTr("Any ENS username")
if (name.startsWith("*."))
return qsTr("ENS username on '%1' domain").arg(name.substring(2))
return qsTr("ENS username '%1'").arg(name)
default:
return ""
}
}
function editPermission(index, holdings, permissions, channels, isPrivate) {
// TO BE REPLACED: Call to backend
createPermission(holdings, permissions, isPrivate, channels, index)

View File

@ -170,6 +170,10 @@ StatusScrollView {
}
return dirty
}
function holdingsTextFormat(type, name, amount) {
return CommunityPermissionsHelpers.setHoldingsTextFormat(type, name, amount)
}
}
contentWidth: mainLayout.width
@ -190,6 +194,9 @@ StatusScrollView {
}
StatusItemSelector {
id: tokensSelector
property int editedIndex
Layout.fillWidth: true
icon: Style.svg("contact_verified")
title: qsTr("Who holds")
@ -198,14 +205,13 @@ StatusScrollView {
asset.height: 28
asset.width: asset.height
addButton.visible: itemsModel.count < d.maxHoldingsItems
property int editedIndex
itemsModel: SortFilterProxyModel {
sourceModel: d.dirtyValues.holdingsModel
proxyRoles: ExpressionRole {
name: "text"
expression: root.store.setHoldingsTextFormat(model.type, model.name, model.amount)
// Direct call for singleton function is not handled properly by SortFilterProxyModel that's why `holdingsTextFormat` is used instead.
expression: d.holdingsTextFormat(model.type, model.name, model.amount)
}
}
@ -222,15 +228,13 @@ StatusScrollView {
}
onAddAsset: {
const modelItem = CommunityPermissionsHelpers.getAssetByKey(
store.assetsModel, key)
const modelItem = CommunityPermissionsHelpers.getTokenByKey(store.assetsModel, key)
addItem(HoldingTypes.Type.Asset, modelItem, amount)
dropdown.close()
}
onAddCollectible: {
const modelItem = CommunityPermissionsHelpers.getCollectibleByKey(
store.collectiblesModel, key)
const modelItem = CommunityPermissionsHelpers.getTokenByKey(store.collectiblesModel, key)
addItem(HoldingTypes.Type.Collectible, modelItem, amount)
dropdown.close()
}
@ -244,8 +248,7 @@ StatusScrollView {
}
onUpdateAsset: {
const modelItem = CommunityPermissionsHelpers.getAssetByKey(
store.assetsModel, key)
const modelItem = CommunityPermissionsHelpers.getTokenByKey(store.assetsModel, key)
const name = modelItem.shortName ? modelItem.shortName : modelItem.name
const imageSource = modelItem.iconSource.toString()
@ -255,8 +258,7 @@ StatusScrollView {
}
onUpdateCollectible: {
const modelItem = CommunityPermissionsHelpers.getCollectibleByKey(
store.collectiblesModel, key)
const modelItem = CommunityPermissionsHelpers.getTokenByKey(store.collectiblesModel, key)
const name = modelItem.name
const imageSource = modelItem.iconSource.toString()
@ -284,7 +286,7 @@ StatusScrollView {
dropdown.parent = tokensSelector.addButton
dropdown.x = tokensSelector.addButton.width + d.dropdownHorizontalOffset
dropdown.y = 0
dropdown.openFlow(HoldingsDropdown.FlowType.Add)
dropdown.open()
}
onItemClicked: {
@ -305,7 +307,6 @@ StatusScrollView {
case HoldingTypes.Type.Collectible:
dropdown.collectibleKey = modelItem.key
dropdown.collectibleAmount = modelItem.amount
dropdown.collectiblesSpecificAmount = modelItem.amount !== 1
break
case HoldingTypes.Type.Ens:
dropdown.ensDomainName = modelItem.name
@ -314,8 +315,8 @@ StatusScrollView {
console.warn("Unsupported holdings type.")
}
dropdown.openFlow(HoldingsDropdown.FlowType.Update)
dropdown.setActiveTab(modelItem.type)
dropdown.openUpdateFlow()
editedIndex = index
}

View File

@ -10,6 +10,7 @@ import utils 1.0
import shared.popups 1.0
import AppLayouts.Chat.controls.community 1.0
import AppLayouts.Chat.helpers 1.0
StatusScrollView {
id: root
@ -23,6 +24,10 @@ StatusScrollView {
QtObject {
id: d
property int permissionIndexToRemove
function holdingsTextFormat(type, name, amount) {
return CommunityPermissionsHelpers.setHoldingsTextFormat(type, name, amount)
}
}
contentWidth: mainLayout.width
@ -47,7 +52,8 @@ StatusScrollView {
proxyRoles: ExpressionRole {
name: "text"
expression: root.store.setHoldingsTextFormat(model.type, model.name, model.amount)
// Direct call for singleton function is not handled properly by SortFilterProxyModel that's why `holdingsTextFormat` is used instead.
expression: d.holdingsTextFormat(model.type, model.name, model.amount)
}
}
permissionName: model.permissionsObjectModel.text