377 lines
12 KiB
QML
377 lines
12 KiB
QML
import QtQuick 2.15
|
||
import QtQuick.Controls 2.15
|
||
import QtQuick.Layouts 1.15
|
||
import QtQml.Models 2.15
|
||
|
||
import StatusQ 0.1
|
||
import StatusQ.Controls 0.1
|
||
import StatusQ.Core 0.1
|
||
import StatusQ.Core.Theme 0.1
|
||
import StatusQ.Core.Utils 0.1
|
||
import StatusQ.Popups.Dialog 0.1
|
||
|
||
import AppLayouts.Wallet.views 1.0
|
||
import shared.controls 1.0
|
||
import utils 1.0
|
||
|
||
import SortFilterProxyModel 0.2
|
||
|
||
/**
|
||
Two-tabs panel holding searchable lists of assets (single level) and
|
||
collectibles (two levels).
|
||
|
||
Structure:
|
||
|
||
TabBar (assets, collectibles)
|
||
StackLayout (current index bound to tab bar's current index)
|
||
Assets List (assets part)
|
||
StackView (collectibles part)
|
||
Collectibles List (top level - groups by collection/community)
|
||
Collectibles List (nested level, on demand)
|
||
*/
|
||
Control {
|
||
id: root
|
||
|
||
enum Tabs {
|
||
Assets = 0,
|
||
Collectibles = 1
|
||
}
|
||
|
||
/**
|
||
Expected model structure:
|
||
|
||
tokensKey [string] - unique asset's identifier
|
||
name [string] - asset's name
|
||
symbol [string] - asset's symbol
|
||
iconSource [url] - asset's icon
|
||
currencyBalanceAsString [string] - formatted balance
|
||
balances [model] - submodel of balances per chain
|
||
balanceAsString [string] - formatted balance per chain
|
||
iconUrl [url] - chain's icon
|
||
**/
|
||
property alias assetsModel: assetsSfpm.sourceModel
|
||
|
||
/**
|
||
Expected model structure:
|
||
|
||
groupName [string] - group name
|
||
icon [url] - icon image of a group
|
||
type [string] - group type, can be "community" or "other"
|
||
subitems [model] - submodel of collectibles/collections of the group
|
||
key [string] - balance
|
||
name [string] - name of the subitem
|
||
balance [int] - balance of the subitem
|
||
icon [url] - icon of the subitem
|
||
**/
|
||
property alias collectiblesModel: collectiblesSfpm.sourceModel
|
||
|
||
// Index of the current tab, indexes correspond to the Tabs enum values.
|
||
property alias currentTab: tabBar.currentIndex
|
||
|
||
signal assetSelected(string key)
|
||
signal collectionSelected(string key)
|
||
signal collectibleSelected(string key)
|
||
|
||
property string highlightedKey: ""
|
||
|
||
SortFilterProxyModel {
|
||
id: assetsSfpm
|
||
|
||
filters: AnyOf {
|
||
SearchFilter {
|
||
roleName: "name"
|
||
searchPhrase: assetsSearchBox.text
|
||
}
|
||
SearchFilter {
|
||
roleName: "symbol"
|
||
searchPhrase: assetsSearchBox.text
|
||
}
|
||
}
|
||
}
|
||
|
||
SortFilterProxyModel {
|
||
id: collectiblesSfpm
|
||
|
||
filters: SearchFilter {
|
||
roleName: "groupName"
|
||
searchPhrase: collectiblesSearchBox.text
|
||
}
|
||
}
|
||
|
||
component SearchFilter: RegExpFilter {
|
||
required property string searchPhrase
|
||
|
||
pattern: `*${searchPhrase}*`
|
||
caseSensitivity : Qt.CaseInsensitive
|
||
syntax: RegExpFilter.Wildcard
|
||
}
|
||
|
||
component Search: SearchBox {
|
||
input.leftPadding: root.leftPadding
|
||
input.rightPadding: root.leftPadding
|
||
minimumHeight: 56
|
||
maximumHeight: 56
|
||
input.showBackground: false
|
||
focus: visible
|
||
}
|
||
|
||
contentItem: ColumnLayout {
|
||
StatusTabBar {
|
||
id: tabBar
|
||
|
||
visible: !!root.assetsModel && !!root.collectiblesModel
|
||
|
||
currentIndex: !!root.assetsModel
|
||
? TokenSelectorPanel.Tabs.Assets
|
||
: TokenSelectorPanel.Tabs.Collectibles
|
||
|
||
StatusTabButton {
|
||
text: qsTr("Assets")
|
||
width: implicitWidth
|
||
|
||
visible: !!root.assetsModel
|
||
}
|
||
|
||
StatusTabButton {
|
||
text: qsTr("Collectibles")
|
||
width: implicitWidth
|
||
|
||
visible: !!root.collectiblesModel
|
||
}
|
||
}
|
||
|
||
StackLayout {
|
||
Layout.maximumHeight: 400
|
||
|
||
visible: !!root.assetsModel || !!root.collectiblesModel
|
||
currentIndex: tabBar.currentIndex
|
||
|
||
ColumnLayout {
|
||
Layout.preferredHeight: visible ? implicitHeight : 0
|
||
spacing: 0
|
||
|
||
Search {
|
||
id: assetsSearchBox
|
||
|
||
Layout.fillWidth: true
|
||
placeholderText: qsTr("Search assets")
|
||
}
|
||
|
||
StatusDialogDivider {
|
||
Layout.fillWidth: true
|
||
visible: assetsListView.count
|
||
}
|
||
|
||
StatusListView {
|
||
id: assetsListView
|
||
|
||
clip: true
|
||
|
||
Layout.fillWidth: true
|
||
Layout.fillHeight: true
|
||
Layout.preferredHeight: contentHeight
|
||
|
||
model: assetsSfpm
|
||
|
||
delegate: TokenSelectorAssetDelegate {
|
||
required property var model
|
||
required property int index
|
||
|
||
highlighted: tokensKey === root.highlightedKey
|
||
|
||
tokensKey: model.tokensKey
|
||
name: model.name
|
||
symbol: model.symbol
|
||
currencyBalanceAsString: model.currencyBalanceAsString
|
||
iconSource: model.iconSource
|
||
balancesModel: model.balances
|
||
|
||
onClicked: root.assetSelected(model.tokensKey)
|
||
}
|
||
}
|
||
}
|
||
|
||
StackView {
|
||
id: collectiblesStackView
|
||
|
||
Layout.fillWidth: true
|
||
Layout.fillHeight: true
|
||
Layout.preferredHeight: visible ? currentItem.implicitHeight : 0
|
||
|
||
initialItem: ColumnLayout {
|
||
spacing: 0
|
||
|
||
Search {
|
||
id: collectiblesSearchBox
|
||
|
||
Layout.fillWidth: true
|
||
placeholderText: qsTr("Search collectibles")
|
||
}
|
||
|
||
StatusDialogDivider {
|
||
Layout.fillWidth: true
|
||
visible: collectiblesListView.count
|
||
}
|
||
|
||
StatusListView {
|
||
id: collectiblesListView
|
||
|
||
Layout.fillWidth: true
|
||
Layout.fillHeight: true
|
||
Layout.preferredHeight: contentHeight
|
||
|
||
clip: true
|
||
model: collectiblesSfpm
|
||
|
||
delegate: TokenSelectorCollectibleDelegate {
|
||
required property var model
|
||
|
||
readonly property int subitemsCount:
|
||
model.subitems.ModelCount.count
|
||
|
||
readonly property bool isCommunity:
|
||
model.type === "community"
|
||
|
||
readonly property bool showCount:
|
||
subitemsCount > 1 || isCommunity
|
||
|
||
name: model.groupName
|
||
balance: showCount ? subitemsCount : ""
|
||
image: model.icon
|
||
goDeeperIconVisible: subitemsCount > 1
|
||
|| isCommunity
|
||
highlighted: subitemsCount === 1 && !isCommunity
|
||
? ModelUtils.get(model.subitems, 0, "key")
|
||
=== root.highlightedKey
|
||
: false
|
||
|
||
onClicked: {
|
||
if (subitemsCount === 1 && !isCommunity) {
|
||
const key = ModelUtils.get(model.subitems, 0, "key")
|
||
root.collectibleSelected(key)
|
||
return
|
||
}
|
||
|
||
const parameters = {
|
||
index: collectiblesSfpm.index(model.index, 0),
|
||
model: model.subitems,
|
||
isCommunity: isCommunity
|
||
}
|
||
|
||
collectiblesStackView.push(
|
||
collectiblesSublistComponent,
|
||
parameters,
|
||
StackView.Immediate)
|
||
}
|
||
}
|
||
|
||
section.property: "type"
|
||
section.delegate: StatusBaseText {
|
||
color: Theme.palette.baseColor1
|
||
topPadding: Style.current.padding
|
||
|
||
text: section === "community"
|
||
? qsTr("Community minted")
|
||
: qsTr("Other")
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
Component {
|
||
id: collectiblesSublistComponent
|
||
|
||
ColumnLayout {
|
||
property var index
|
||
property alias model: sublistSfpm.sourceModel
|
||
property bool isCommunity
|
||
|
||
spacing: 0
|
||
|
||
SortFilterProxyModel {
|
||
id: sublistSfpm
|
||
|
||
filters: SearchFilter {
|
||
roleName: "name"
|
||
searchPhrase: collectiblesSublistSearchBox.text
|
||
}
|
||
}
|
||
|
||
StatusIconTextButton {
|
||
id: backButton
|
||
|
||
statusIcon: "previous"
|
||
icon.width: 12
|
||
icon.height: 12
|
||
text: qsTr("Back")
|
||
|
||
onClicked: collectiblesStackView.pop(StackView.Immediate)
|
||
}
|
||
|
||
StatusDialogDivider {
|
||
Layout.fillWidth: true
|
||
visible: collectiblesListView.count
|
||
}
|
||
|
||
Search {
|
||
id: collectiblesSublistSearchBox
|
||
|
||
Layout.fillWidth: true
|
||
placeholderText: qsTr("Search collectibles")
|
||
}
|
||
|
||
StatusDialogDivider {
|
||
Layout.fillWidth: true
|
||
visible: collectiblesListView.count
|
||
}
|
||
|
||
StatusListView {
|
||
id: sublist
|
||
|
||
Layout.fillWidth: true
|
||
Layout.fillHeight: true
|
||
Layout.preferredHeight: contentHeight
|
||
|
||
model: sublistSfpm
|
||
|
||
clip: true
|
||
|
||
delegate: TokenSelectorCollectibleDelegate {
|
||
required property var model
|
||
|
||
name: model.name
|
||
balance: model.balance > 1 ? model.balance : ""
|
||
image: model.icon
|
||
goDeeperIconVisible: false
|
||
highlighted: model.key === root.highlightedKey
|
||
|
||
onClicked: {
|
||
if (isCommunity)
|
||
root.collectionSelected(model.key)
|
||
else
|
||
root.collectibleSelected(model.key)
|
||
}
|
||
}
|
||
}
|
||
|
||
// Detection if the related model entry has been removed.
|
||
// Using model.Component.destruction.connect is not reliable because
|
||
// is not called for submodels maintained in c++ by the parent model.
|
||
ItemSelectionModel {
|
||
id: selection
|
||
|
||
model: collectiblesSfpm
|
||
|
||
onHasSelectionChanged: {
|
||
if (!hasSelection)
|
||
collectiblesStackView.pop(StackView.Immediate)
|
||
}
|
||
|
||
Component.onCompleted: select(index, ItemSelectionModel.Select)
|
||
}
|
||
}
|
||
}
|
||
}
|