import QtQuick 2.15 import QtQuick.Layouts 1.15 import QtQml 2.15 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 bool isENSTab: true property bool isCollectiblesOnly: false 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 = root.isCollectiblesOnly ? HoldingTypes.Type.Collectible : holdingType } function reset() { d.currentHoldingType = root.isCollectiblesOnly ? HoldingTypes.Type.Collectible : 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 var tabsModel: [qsTr("Assets"), qsTr("Collectibles"), qsTr("ENS")] readonly property var tabsModelNoEns: [qsTr("Assets"), qsTr("Collectibles")] 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: root.isCollectiblesOnly ? HoldingTypes.Type.Collectible : 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 readonly property int bottomInset: 20 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 } function forceLayout() { root.height = 0 //setting height to 0 before because Popup cannot properly resize if the current contentHeight exceeds the available height root.height = undefined //use implicit height } } StatesStack { id: statesStack } width: d.defaultWidth padding: d.padding bottomInset: d.bottomInset bottomPadding: d.padding + d.bottomInset contentItem: ColumnLayout { id: content spacing: d.backButtonToContentSpace state: statesStack.currentState 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: !root.isCollectiblesOnly && !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: d; extendedDropdownType: ExtendedDropdownContent.Type.Assets} }, State { name: HoldingTypes.Type.Collectible PropertyChanges {target: loader; sourceComponent: listLayout} PropertyChanges {target: d; extendedDropdownType: ExtendedDropdownContent.Type.Collectibles} }, State { name: HoldingTypes.Type.Ens PropertyChanges {target: loader; sourceComponent: ensLayout} } ] onCurrentIndexChanged: { if(currentIndex >= 0) { d.currentHoldingType = root.isCollectiblesOnly ? HoldingTypes.Type.Collectible : d.holdingTypes[currentIndex] d.setInitialFlow() } } Repeater { id: tabLabelsRepeater model: root.isENSTab ? d.tabsModel : d.tabsModelNoEns StatusSwitchTabButton { text: modelData fontPixelSize: d.tabBarTextSize } } } Loader { id: loader Layout.fillWidth: true Layout.fillHeight: true onItemChanged: d.forceLayout() } states: [ State { name: HoldingsDropdown.FlowType.Selected PropertyChanges {target: loader; sourceComponent: (d.currentHoldingType === HoldingTypes.Type.Asset) ? assetLayout : ((d.currentHoldingType === HoldingTypes.Type.Collectible) ? collectibleLayout : ensLayout) } }, State { name: HoldingsDropdown.FlowType.List_Deep1 PropertyChanges {target: loader; sourceComponent: listLayout} PropertyChanges {target: d; extendedDeepNavigation: false} }, State { name: HoldingsDropdown.FlowType.List_Deep2 extend: HoldingsDropdown.FlowType.List_Deep1 PropertyChanges {target: d; extendedDeepNavigation: true} } ] } onClosed: root.reset() onIsCollectiblesOnlyChanged: root.reset() onIsENSTabChanged: root.reset() Component { id: listLayout ExtendedDropdownContent { id: listPanel assetsModel: root.assetsModel collectiblesModel: root.collectiblesModel checkedKeys: root.usedTokens.map(entry => entry.key) type: d.extendedDropdownType onTypeChanged: forceActiveFocus() 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) } onLayoutChanged: d.forceLayout() 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() } } }