status-desktop/ui/app/AppLayouts/Wallet/panels/SearchableCollectiblesPanel...

297 lines
9.2 KiB
QML
Raw Normal View History

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 utils 1.0
import SortFilterProxyModel 0.2
/**
Panel holding search field and two-levels list of collectibles.
*/
Control {
id: root
/**
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 model: sfpm.sourceModel
signal collectionSelected(string key)
signal collectibleSelected(string key)
property string highlightedKey: ""
readonly property alias currentItem: collectiblesStackView.currentItem
function clearSearch() {
collectiblesSearchBox.text = ""
}
SortFilterProxyModel {
id: sfpm
filters: SearchFilter {
roleName: "groupName"
searchPhrase: collectiblesSearchBox.text
}
}
contentItem: StackView {
id: collectiblesStackView
implicitHeight: currentItem.implicitHeight
initialItem: ColumnLayout {
spacing: 0
StatusBaseText {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: 4
text: qsTr("Your collectibles will appear here")
color: Theme.palette.baseColor1
visible: !collectiblesListView.count && !collectiblesSearchBox.text
}
TokenSearchBox {
id: collectiblesSearchBox
objectName: "collectiblesSearchBox"
Layout.fillWidth: true
placeholderText: qsTr("Search collectibles")
visible: collectiblesListView.count || !!collectiblesSearchBox.text
Keys.forwardTo: [collectiblesListView]
}
StatusDialogDivider {
Layout.fillWidth: true
visible: collectiblesListView.count
}
StatusListView {
id: collectiblesListView
readonly property bool validSearchResultExists: !!collectiblesSearchBox.text && sfpm.count > 0
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: contentHeight
Layout.leftMargin: 4
Layout.rightMargin: 4
spacing: 4
clip: true
model: sfpm
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
networkIcon: model.iconUrl
highlighted: subitemsCount === 1 && !isCommunity
? ModelUtils.get(model.subitems, 0, "key")
=== root.highlightedKey
: false
isAutoHovered: collectiblesListView.validSearchResultExists && model.index === 0 && !collectiblesListViewHoverHandler.hovered
onClicked: {
if (subitemsCount === 1 && !isCommunity) {
const key = ModelUtils.get(model.subitems, 0, "key")
root.collectibleSelected(key)
return
}
const parameters = {
index: sfpm.index(model.index, 0),
model: model.subitems,
isCommunity: isCommunity
}
collectiblesStackView.push(
collectiblesSublistComponent,
parameters,
StackView.Immediate)
}
}
section.property: "type"
section.delegate: TokenSelectorSectionDelegate {
width: ListView.view.width
text: section === "community"
? qsTr("Community minted")
: qsTr("Other")
}
Keys.onReturnPressed: {
if(validSearchResultExists)
collectiblesListView.itemAtIndex(0).clicked()
}
Keys.onEnterPressed: {
if(validSearchResultExists)
collectiblesListView.itemAtIndex(0).clicked()
}
HoverHandler {
id: collectiblesListViewHoverHandler
}
}
}
}
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")
horizontalPadding: 21
bottomPadding: Theme.halfPadding
onClicked: collectiblesStackView.pop(StackView.Immediate)
}
StatusDialogDivider {
Layout.fillWidth: true
visible: collectiblesListView.count
}
TokenSearchBox {
id: collectiblesSublistSearchBox
Layout.fillWidth: true
placeholderText: qsTr("Search collectibles")
Keys.forwardTo: [sublist]
}
StatusDialogDivider {
Layout.fillWidth: true
visible: collectiblesListView.count
}
StatusListView {
id: sublist
readonly property bool validSearchResultExists: !!collectiblesSublistSearchBox.text && sublistSfpm.count > 0
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: contentHeight
Layout.leftMargin: 4
Layout.rightMargin: 4
model: sublistSfpm
clip: true
delegate: TokenSelectorCollectibleDelegate {
required property var model
name: model.name
balance: model.balance > 1 ? model.balance : ""
image: model.icon
goDeeperIconVisible: false
networkIcon: model.iconUrl
highlighted: model.key === root.highlightedKey
isAutoHovered: sublist.validSearchResultExists && model.index === 0 && !sublistHoverHandler.hovered
onClicked: {
if (isCommunity)
root.collectionSelected(model.key)
else
root.collectibleSelected(model.key)
}
}
Keys.onReturnPressed: {
if(validSearchResultExists)
sublist.itemAtIndex(0).clicked()
}
Keys.onEnterPressed: {
if(validSearchResultExists)
sublist.itemAtIndex(0).clicked()
}
HoverHandler {
id: sublistHoverHandler
}
}
// 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: sfpm
onHasSelectionChanged: {
if (!hasSelection)
collectiblesStackView.pop(StackView.Immediate)
}
Component.onCompleted: select(index, ItemSelectionModel.Select)
}
}
}
}