status-desktop/ui/app/AppLayouts/Chat/controls/community/HoldingsDropdown.qml

405 lines
13 KiB
QML

import QtQuick 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Utils 0.1
import AppLayouts.Chat.helpers 1.0
StatusDropdown {
id: root
property var assetsModel
property var collectiblesModel
property var usedTokens: []
property var usedEnsNames: []
property string assetKey: ""
property real assetAmount: 0
property string collectibleKey: ""
property real collectibleAmount: 1
property string ensDomainName: ""
signal addAsset(string key, real amount)
signal addCollectible(string key, real amount)
signal addEns(string domain)
signal updateAsset(string key, real amount)
signal updateCollectible(string key, real amount)
signal updateEns(string domain)
signal removeClicked
enum FlowType {
Selected, List_Deep1, List_Deep2
}
function openUpdateFlow() {
d.initialHoldingMode = HoldingTypes.Mode.UpdateOrRemove
if(d.currentHoldingType !== HoldingTypes.Type.Ens) {
if(statesStack.size === 0)
statesStack.push(HoldingsDropdown.FlowType.List_Deep1)
statesStack.push(HoldingsDropdown.FlowType.Selected)
}
open()
}
function setActiveTab(holdingType) {
d.currentHoldingType = holdingType
}
function reset() {
d.currentHoldingType = HoldingTypes.Type.Asset
d.initialHoldingMode = HoldingTypes.Mode.Add
root.assetKey = ""
root.collectibleKey = ""
root.ensDomainName = ""
d.setDefaultAmounts()
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
property int extendedDropdownType: ExtendedDropdownContent.Type.Assets
property int currentHoldingType: HoldingTypes.Type.Asset
property bool updateSelected: false
property int initialHoldingMode: HoldingTypes.Mode.Add
property int effectiveHoldingMode: initialHoldingMode === HoldingTypes.Mode.UpdateOrRemove
? HoldingTypes.Mode.UpdateOrRemove
: (updateSelected ? HoldingTypes.Mode.Update : HoldingTypes.Mode.Add)
property bool extendedDeepNavigation: false
property var currentSubItems
property string currentItemKey: ""
property string assetAmountText: ""
property string collectibleAmountText: "1"
property bool ensDomainNameValid: false
// By design values:
readonly property int padding: 8
readonly property int defaultWidth: 289
readonly property int extendedContentHeight: 380
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
function setInitialFlow() {
statesStack.clear()
if(d.currentHoldingType !== HoldingTypes.Type.Ens)
statesStack.push(HoldingsDropdown.FlowType.List_Deep1)
else
statesStack.push(HoldingsDropdown.FlowType.Selected)
}
function setDefaultAmounts() {
d.assetAmountText = ""
d.collectibleAmountText = ""
root.assetAmount = 0
root.collectibleAmount = 1
}
}
QtObject {
id: statesStack
property alias currentState: content.state
property int size: 0
property var states: []
function push(state) {
states.push(state)
currentState = state
size++
}
function pop() {
states.pop()
currentState = states.length ? states[states.length - 1] : ""
size--
}
function clear() {
currentState = ""
size = 0
states = []
}
}
width: d.defaultWidth
padding: d.padding
margins: 0 // force keeping within the bounds of the enclosing window
contentItem: ColumnLayout {
id: content
spacing: d.backButtonToContentSpace
StatusIconTextButton {
id: backButton
Layout.preferredWidth: d.backButtonWidth
Layout.preferredHeight: d.backButtonHeight
visible: statesStack.size > 1
spacing: 0
leftPadding: 4
statusIcon: "next"
icon.width: 12
icon.height: 12
iconRotation: 180
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("Assets"), qsTr("Collectibles"), qsTr("ENS")]
StatusSwitchTabButton {
text: modelData
fontPixelSize: d.tabBarTextSize
}
}
}
Loader {
id: loader
Layout.fillWidth: true
Layout.fillHeight: true
}
states: [
State {
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: HoldingsDropdown.FlowType.List_Deep1
PropertyChanges {target: loader; sourceComponent: listLayout}
PropertyChanges {target: root; height: d.extendedContentHeight}
PropertyChanges {target: d; extendedDeepNavigation: false}
},
State {
name: HoldingsDropdown.FlowType.List_Deep2
extend: HoldingsDropdown.FlowType.List_Deep1
PropertyChanges {target: d; extendedDeepNavigation: true}
}
]
}
onClosed: root.reset()
Component {
id: listLayout
ExtendedDropdownContent {
id: listPanel
assetsModel: root.assetsModel
collectiblesModel: root.collectiblesModel
checkedKeys: root.usedTokens.map(entry => entry.key)
type: d.extendedDropdownType
onItemClicked: {
d.assetAmountText = ""
d.collectibleAmountText = ""
if (checkedKeys.includes(key)) {
const amount = root.usedTokens.find(entry => entry.key === key).amount
if(d.extendedDropdownType === ExtendedDropdownContent.Type.Assets)
root.assetAmount = amount
else
root.collectibleAmount = amount
d.updateSelected = true
} else {
d.setDefaultAmounts()
d.updateSelected = false
}
if(d.extendedDropdownType === ExtendedDropdownContent.Type.Assets)
root.assetKey = key
else
root.collectibleKey = key
statesStack.push(HoldingsDropdown.FlowType.Selected)
}
onNavigateDeep: {
d.currentSubItems = subItems
d.currentItemKey = key
statesStack.push(HoldingsDropdown.FlowType.List_Deep2)
}
Component.onCompleted: {
if(d.extendedDeepNavigation)
listPanel.goForward(d.currentItemKey,
CommunityPermissionsHelpers.getTokenNameByKey(root.collectiblesModel, d.currentItemKey),
CommunityPermissionsHelpers.getTokenIconByKey(root.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: assetLayout
TokenPanel {
id: assetPanel
readonly property real effectiveAmount: amountValid ? amount : 0
tokenName: CommunityPermissionsHelpers.getTokenNameByKey(root.assetsModel, root.assetKey)
tokenShortName: CommunityPermissionsHelpers.getTokenShortNameByKey(root.assetsModel, root.assetKey)
tokenImage: CommunityPermissionsHelpers.getTokenIconByKey(root.assetsModel, root.assetKey)
amountText: d.assetAmountText
tokenCategoryText: qsTr("Asset")
addOrUpdateButtonEnabled: d.assetsReady
mode: d.effectiveHoldingMode
onEffectiveAmountChanged: root.assetAmount = effectiveAmount
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)
assetPanel.setAmount(root.assetAmount)
}
Connections {
target: backButton
function onClicked() { statesStack.pop() }
}
}
}
Component {
id: collectibleLayout
TokenPanel {
id: collectiblePanel
readonly property real effectiveAmount: amountValid ? amount : 0
tokenName: CommunityPermissionsHelpers.getTokenNameByKey(root.collectiblesModel, root.collectibleKey)
tokenShortName: ""
tokenImage: CommunityPermissionsHelpers.getTokenIconByKey(root.collectiblesModel, root.collectibleKey)
amountText: d.collectibleAmountText
tokenCategoryText: qsTr("Collectible")
addOrUpdateButtonEnabled: d.collectiblesReady
allowDecimals: false
mode: d.effectiveHoldingMode
onEffectiveAmountChanged: root.collectibleAmount = effectiveAmount
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)
collectiblePanel.setAmount(root.collectibleAmount)
}
Connections {
target: backButton
function onClicked() { statesStack.pop() }
}
}
}
Component {
id: ensLayout
EnsPanel {
addButtonEnabled: d.ensReady
domainName: root.ensDomainName
mode: d.initialHoldingMode
reservedNames: root.usedEnsNames
onDomainNameChanged: root.ensDomainName = domainName
onDomainNameValidChanged: d.ensDomainNameValid = domainNameValid
onAddClicked: root.addEns(root.ensDomainName)
onUpdateClicked: root.updateEns(root.ensDomainName)
onRemoveClicked: root.removeClicked()
}
}
}