327 lines
12 KiB
QML
327 lines
12 KiB
QML
|
|
import QtQml 2.15
|
|
import QtQuick 2.13
|
|
import QtQuick.Layouts 1.13
|
|
|
|
import shared.controls 1.0
|
|
import utils 1.0
|
|
|
|
import SortFilterProxyModel 0.2
|
|
|
|
import StatusQ 0.1
|
|
import StatusQ.Controls 0.1
|
|
import StatusQ.Core 0.1
|
|
import StatusQ.Core.Theme 0.1
|
|
|
|
import "../controls"
|
|
|
|
Item {
|
|
id: root
|
|
property var assetsModel
|
|
property var collectiblesModel
|
|
property var networksModel
|
|
property string currentCurrencySymbol
|
|
property bool onlyAssets: true
|
|
|
|
implicitWidth: holdingItemSelector.implicitWidth
|
|
implicitHeight: holdingItemSelector.implicitHeight
|
|
|
|
property var searchAssetSymbolByAddressFn: function (address) {
|
|
return ""
|
|
}
|
|
|
|
signal itemHovered(string holdingId, var holdingType)
|
|
signal itemSelected(string holdingId, var holdingType)
|
|
|
|
property alias selectedItem: holdingItemSelector.selectedItem
|
|
property alias hoveredItem: holdingItemSelector.hoveredItem
|
|
|
|
function setSelectedItem(item, holdingType) {
|
|
d.browsingHoldingType = holdingType
|
|
holdingItemSelector.selectedItem = null
|
|
d.currentHoldingType = holdingType
|
|
holdingItemSelector.selectedItem = item
|
|
}
|
|
|
|
function setHoveredItem(item, holdingType) {
|
|
d.browsingHoldingType = holdingType
|
|
holdingItemSelector.hoveredItem = null
|
|
d.currentHoldingType = holdingType
|
|
holdingItemSelector.hoveredItem = item
|
|
}
|
|
|
|
QtObject {
|
|
id: d
|
|
// Internal management properties and signals:
|
|
readonly property var holdingTypes: onlyAssets ?
|
|
[Constants.HoldingType.Asset] :
|
|
[Constants.HoldingType.Asset, Constants.HoldingType.Collectible]
|
|
|
|
readonly property var tabsModel: onlyAssets ?
|
|
[qsTr("Assets")] :
|
|
[qsTr("Assets"), qsTr("Collectibles")]
|
|
|
|
readonly property var updateSearchText: Backpressure.debounce(root, 1000, function(inputText) {
|
|
searchText = inputText
|
|
})
|
|
|
|
function isAsset(type) {
|
|
return type === Constants.HoldingType.Asset
|
|
}
|
|
|
|
property int browsingHoldingType: Constants.HoldingType.Asset
|
|
readonly property bool isCurrentBrowsingTypeAsset: isAsset(browsingHoldingType)
|
|
readonly property bool isBrowsingCollection: !isCurrentBrowsingTypeAsset && !!collectiblesModel && collectiblesModel.currentCollectionUid !== ""
|
|
property string currentBrowsingCollectionName
|
|
|
|
property var currentHoldingType: Constants.HoldingType.Unknown
|
|
|
|
property string searchText
|
|
readonly property string assetSymbolByAddress: isCurrentBrowsingTypeAsset ? "": root.searchAssetSymbolByAddressFn(searchText)
|
|
readonly property string uppercaseSearchText: searchText.toUpperCase()
|
|
|
|
property var assetTextFn: function (asset) {
|
|
return !!asset && asset.symbol ? asset.symbol : ""
|
|
}
|
|
|
|
property var assetIconSourceFn: function (asset) {
|
|
return !!asset && asset.symbol ? Style.png("tokens/%1".arg(asset.symbol)) : ""
|
|
}
|
|
|
|
property var assetComboBoxModel: SortFilterProxyModel {
|
|
sourceModel: root.assetsModel
|
|
filters: [
|
|
ExpressionFilter {
|
|
expression: {
|
|
d.uppercaseSearchText; // Force re-evaluation when searchText changes
|
|
return (
|
|
d.uppercaseSearchText === "" ||
|
|
symbol.startsWith(d.uppercaseSearchText) ||
|
|
name.toUpperCase().startsWith(d.uppercaseSearchText) |
|
|
(d.assetSymbolByAddress !== "" && symbol.startsWith(d.assetSymbolByAddress))
|
|
)
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
property var collectibleTextFn: function (item) {
|
|
if (!!item) {
|
|
return !!item.collectionName ? item.collectionName + ": " + item.name : item.name
|
|
}
|
|
return ""
|
|
}
|
|
|
|
property var collectibleIconSourceFn: function (item) {
|
|
return !!item && item.iconUrl ? item.iconUrl : ""
|
|
}
|
|
|
|
readonly property RolesRenamingModel renamedAllNetworksModel: RolesRenamingModel {
|
|
sourceModel: root.networksModel
|
|
mapping: RoleRename {
|
|
from: "iconUrl"
|
|
to: "networkIconUrl"
|
|
}
|
|
}
|
|
|
|
readonly property LeftJoinModel collectibleNetworksJointModel: LeftJoinModel {
|
|
leftModel: root.collectiblesModel
|
|
rightModel: d.renamedAllNetworksModel
|
|
joinRole: "chainId"
|
|
}
|
|
|
|
property var collectibleComboBoxModel: SortFilterProxyModel {
|
|
sourceModel: d.collectibleNetworksJointModel
|
|
filters: [
|
|
ExpressionFilter {
|
|
expression: {
|
|
return d.uppercaseSearchText === "" || name.toUpperCase().startsWith(d.uppercaseSearchText)
|
|
}
|
|
}
|
|
]
|
|
sorters: RoleSorter {
|
|
roleName: "isCollection"
|
|
sortOrder: Qt.DescendingOrder
|
|
}
|
|
}
|
|
|
|
readonly property string searchPlaceholderText: {
|
|
if (isCurrentBrowsingTypeAsset) {
|
|
return qsTr("Search for token or enter token address")
|
|
} else if (isBrowsingCollection) {
|
|
return qsTr("Search %1").arg(d.currentBrowsingCollectionName ?? qsTr("collectibles in collection"))
|
|
} else {
|
|
return qsTr("Search collectibles")
|
|
}
|
|
}
|
|
|
|
// By design values:
|
|
readonly property int padding: 16
|
|
readonly property int headerTopMargin: 5
|
|
readonly property int tabBarTopMargin: 20
|
|
readonly property int tabBarHeight: 35
|
|
readonly property int bottomInset: 20
|
|
readonly property int assetContentIconSize: 21
|
|
readonly property int collectibleContentIconSize: 28
|
|
readonly property int assetContentTextSize: 28
|
|
readonly property int collectibleContentTextSize: 15
|
|
}
|
|
|
|
HoldingItemSelector {
|
|
id: holdingItemSelector
|
|
width: parent.width
|
|
height: parent.height
|
|
|
|
defaultIconSource: Style.png("tokens/DEFAULT-TOKEN@3x")
|
|
placeholderText: d.isCurrentBrowsingTypeAsset ? qsTr("Select token") : qsTr("Select collectible")
|
|
comboBoxDelegate: Item {
|
|
property var itemModel: model // read 'model' from the delegate's context
|
|
width: loader.width
|
|
height: loader.height
|
|
Loader {
|
|
id: loader
|
|
|
|
// inject model properties to the loaded item's context
|
|
// common
|
|
property var model: itemModel
|
|
property var chainId: model.chainId
|
|
property var name: model.name
|
|
// asset
|
|
property var symbol: model.symbol
|
|
property var totalBalance: model.totalBalance
|
|
property var totalCurrencyBalance: model.totalCurrencyBalance
|
|
property var decimals: model.decimals
|
|
property var balances: model.balances
|
|
// collectible
|
|
property var uid: model.uid
|
|
property var iconUrl: model.iconUrl
|
|
property var networkIconUrl: model.networkIconUrl
|
|
property var collectionUid: model.collectionUid
|
|
property var collectionName: model.collectionName
|
|
property var isCollection: model.isCollection
|
|
|
|
sourceComponent: d.isCurrentBrowsingTypeAsset ? assetComboBoxDelegate : collectibleComboBoxDelegate
|
|
}
|
|
}
|
|
|
|
comboBoxPopupHeader: headerComponent
|
|
itemTextFn: d.isCurrentBrowsingTypeAsset ? d.assetTextFn : d.collectibleTextFn
|
|
itemIconSourceFn: d.isCurrentBrowsingTypeAsset ? d.assetIconSourceFn : d.collectibleIconSourceFn
|
|
comboBoxModel: d.isCurrentBrowsingTypeAsset ? d.assetComboBoxModel : d.collectibleComboBoxModel
|
|
|
|
contentIconSize: d.isAsset(d.currentHoldingType) ? d.assetContentIconSize : d.collectibleContentIconSize
|
|
contentTextSize: d.isAsset(d.currentHoldingType) ? d.assetContentTextSize : d.collectibleContentTextSize
|
|
}
|
|
|
|
Component {
|
|
id: headerComponent
|
|
ColumnLayout {
|
|
width: holdingItemSelector.comboBoxControl.popup.width
|
|
Layout.topMargin: d.headerTopMargin
|
|
spacing: -1 // Used to overlap rectangles from row components
|
|
|
|
StatusTabBar {
|
|
id: tabBar
|
|
|
|
visible: !root.onlyAssets
|
|
Layout.preferredHeight: d.tabBarHeight
|
|
Layout.fillWidth: true
|
|
Layout.leftMargin: d.padding
|
|
Layout.rightMargin: d.padding
|
|
Layout.topMargin: d.tabBarTopMargin
|
|
Layout.bottomMargin: 6
|
|
currentIndex: d.holdingTypes.indexOf(d.browsingHoldingType)
|
|
|
|
onCurrentIndexChanged: {
|
|
if (currentIndex >= 0) {
|
|
d.browsingHoldingType = d.holdingTypes[currentIndex]
|
|
}
|
|
}
|
|
|
|
Repeater {
|
|
id: tabLabelsRepeater
|
|
model: d.tabsModel
|
|
|
|
StatusTabButton {
|
|
text: modelData
|
|
width: implicitWidth
|
|
}
|
|
}
|
|
}
|
|
CollectibleBackButtonWithInfo {
|
|
Layout.fillWidth: true
|
|
visible: d.isBrowsingCollection
|
|
count: collectiblesModel.count
|
|
name: d.currentBrowsingCollectionName
|
|
onBackClicked: {
|
|
if (!d.isCurrentBrowsingTypeAsset) {
|
|
root.collectiblesModel.currentCollectionUid = ""
|
|
}
|
|
}
|
|
}
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: searchInput.input.implicitHeight
|
|
|
|
color: "transparent"
|
|
border.color: Theme.palette.baseColor2
|
|
border.width: 1
|
|
|
|
StatusInput {
|
|
id: searchInput
|
|
anchors.fill: parent
|
|
|
|
input.showBackground: false
|
|
placeholderText: d.searchPlaceholderText
|
|
onTextChanged: Qt.callLater(d.updateSearchText, text)
|
|
input.clearable: true
|
|
input.implicitHeight: 56
|
|
input.rightComponent: StatusFlatRoundButton {
|
|
icon.name: "search"
|
|
type: StatusFlatRoundButton.Type.Secondary
|
|
enabled: false
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: assetComboBoxDelegate
|
|
TokenBalancePerChainDelegate {
|
|
objectName: "AssetSelector_ItemDelegate_" + symbol
|
|
width: holdingItemSelector.comboBoxControl.popup.width
|
|
balancesModel: LeftJoinModel {
|
|
leftModel: balances
|
|
rightModel: root.networksModel
|
|
joinRole: "chainId"
|
|
}
|
|
onTokenSelected: {
|
|
holdingItemSelector.selectedItem = selectedToken
|
|
d.currentHoldingType = Constants.HoldingType.Asset
|
|
root.itemSelected(selectedToken.symbol, Constants.HoldingType.Asset)
|
|
holdingItemSelector.comboBoxControl.popup.close()
|
|
}
|
|
}
|
|
}
|
|
|
|
Component {
|
|
id: collectibleComboBoxDelegate
|
|
CollectibleNestedDelegate {
|
|
objectName: "CollectibleSelector_ItemDelegate_" + collectionUid
|
|
width: holdingItemSelector.comboBoxControl.popup.width
|
|
onItemSelected: {
|
|
if (isCollection) {
|
|
d.currentBrowsingCollectionName = collectionName
|
|
root.collectiblesModel.currentCollectionUid = collectionUid
|
|
} else {
|
|
holdingItemSelector.selectedItem = selectedItem
|
|
d.currentHoldingType = Constants.HoldingType.Collectible
|
|
root.itemSelected(selectedItem.uid, Constants.HoldingType.Collectible)
|
|
holdingItemSelector.comboBoxControl.popup.close()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|