status-desktop/ui/app/AppLayouts/Wallet/panels/ManageTokensPanel.qml
Lukáš Tinkl 7183621369 feat: Manage tokens panel UI component & model
- implements the UI component to manage regular & community tokens
(drag'n'drop or context menu to reorder, show/hide, group by community)
- implements a custom C++ QAIM model which acts as a fake proxy model
for the above QML panel (internally it does all the
sorting/grouping/hiding and preserves the custom sort order)
- adds and corrects support for cascading submenus in StatusAction, and
StatusMenu[Item]
- adds support for mirrored (horizontally flipped) StatusSwitch
- adds a new SortOrderComboBox.qml (this was being used in the first
Figma version, can be ignored now, will be used by the main wallet view
later)
- some minor fixes and cleanups in the used components

Iterates #12377
Closes #12587
2023-11-09 11:45:32 +01:00

379 lines
14 KiB
QML

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Popups 0.1
import StatusQ.Models 0.1
import utils 1.0
import shared.controls 1.0
import AppLayouts.Wallet.controls 1.0
Control {
id: root
required property var baseModel
readonly property bool dirty: d.controller.dirty
background: null
function saveSettings() {
d.controller.saveSettings();
}
function revert() {
d.controller.revert();
}
function clearSettings() {
d.controller.clearSettings();
}
QtObject {
id: d
property bool communityGroupsExpanded: true
readonly property var controller: ManageTokensController {
sourceModel: root.baseModel
arrangeByCommunity: switchArrangeByCommunity.checked
settingsKey: "WalletAssets"
}
}
component CommunityTag: InformationTag {
tagPrimaryLabel.font.weight: Font.Medium
customBackground: Component {
Rectangle {
color: Theme.palette.baseColor4
radius: 20
}
}
}
component LocalTokenDelegate: DropArea {
id: delegateRoot
property int visualIndex: index
property alias dragEnabled: delegate.dragEnabled
property alias bgColor: delegate.bgColor
property alias topInset: delegate.topInset
property alias bottomInset: delegate.bottomInset
property bool isGrouped
property bool isHidden
property int count
ListView.onRemove: SequentialAnimation {
PropertyAction { target: delegateRoot; property: "ListView.delayRemove"; value: true }
NumberAnimation { target: delegateRoot; property: "scale"; to: 0; easing.type: Easing.InOutQuad }
PropertyAction { target: delegateRoot; property: "ListView.delayRemove"; value: false }
}
width: ListView.view.width
height: visible ? delegate.height : 0
onEntered: function(drag) {
var from = drag.source.visualIndex
var to = delegate.visualIndex
if (to === from)
return
//console.warn("!!! DROP from/to", from, to)
ListView.view.model.moveItem(from, to)
drag.accept()
}
StatusDraggableListItem {
id: delegate
visualIndex: index
dragParent: root
Drag.keys: delegateRoot.keys
draggable: true
width: delegateRoot.width
title: model.name// + " (%1 -> %2)".arg(index).arg(model.customSortOrderNo)
secondaryTitle: hovered || menuBtn.menuVisible ? "%1 <b>·</b> %2".arg(LocaleUtils.currencyAmountToLocaleString(model.enabledNetworkBalance))
.arg(LocaleUtils.currencyAmountToLocaleString(model.enabledNetworkCurrencyBalance))
: LocaleUtils.currencyAmountToLocaleString(model.enabledNetworkBalance)
hasImage: true
icon.source: model.imageUrl || Constants.tokenIcon(model.symbol)
icon.width: 32
icon.height: 32
spacing: 12
actions: [
CommunityTag {
tagPrimaryLabel.text: model.communityName
visible: !!model.communityId && !delegateRoot.isGrouped
image.source: model.communityImage
},
ManageTokenMenuButton {
id: menuBtn
currentIndex: visualIndex
count: delegateRoot.count
inHidden: delegateRoot.isHidden
groupId: model.communityId
isCommunityAsset: !!model.communityId
onMoveRequested: (from, to) => isCommunityAsset ? d.controller.communityTokensModel.moveItem(from, to)
: d.controller.regularTokensModel.moveItem(from, to)
onShowHideRequested: (index, flag) => isCommunityAsset ? d.controller.showHideCommunityToken(index, flag)
: d.controller.showHideRegularToken(index, flag)
onShowHideGroupRequested: (groupId, flag) => d.controller.showHideGroup(groupId, flag)
}
]
}
}
component LocalTokenGroupDelegate: DropArea {
id: communityDelegateRoot
property int visualIndex: index
readonly property string communityId: model.communityId
readonly property int childCount: model.enabledNetworkBalance // NB using "balance" as "count" in m_communityTokenGroupsModel
ListView.onRemove: SequentialAnimation {
PropertyAction { target: communityDelegateRoot; property: "ListView.delayRemove"; value: true }
NumberAnimation { target: communityDelegateRoot; property: "scale"; to: 0; easing.type: Easing.InOutQuad }
PropertyAction { target: communityDelegateRoot; property: "ListView.delayRemove"; value: false }
}
keys: ["x-status-draggable-community-group-item"]
visible: childCount
width: ListView.view.width
height: visible ? groupedCommunityTokenDelegate.implicitHeight : 0
onEntered: function(drag) {
var from = drag.source.visualIndex
var to = groupedCommunityTokenDelegate.visualIndex
if (to === from)
return
//console.warn("!!! DROP GROUP from/to", from, to)
ListView.view.model.moveItem(from, to)
drag.accept()
}
StatusDraggableListItem {
id: groupedCommunityTokenDelegate
width: parent.width
height: dragActive ? implicitHeight : parent.height
leftPadding: Style.current.halfPadding
rightPadding: Style.current.halfPadding
bottomPadding: Style.current.halfPadding
topPadding: 22
draggable: true
spacing: 12
bgColor: Theme.palette.baseColor4
visualIndex: index
dragParent: root
Drag.keys: communityDelegateRoot.keys
contentItem: ColumnLayout {
spacing: 0
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: 12
Layout.rightMargin: 12
Layout.bottomMargin: 14
spacing: groupedCommunityTokenDelegate.spacing
StatusIcon {
Layout.preferredWidth: 20
Layout.preferredHeight: 20
icon: "justify"
color: Theme.palette.baseColor1
}
StatusRoundedImage {
radius: groupedCommunityTokenDelegate.bgRadius
Layout.preferredWidth: 32
Layout.preferredHeight: 32
image.source: model.communityImage
showLoadingIndicator: true
image.fillMode: Image.PreserveAspectCrop
}
StatusBaseText {
text: model.communityName// + "(%1 -> %2)".arg(index).arg(model.customSortOrderNo)
elide: Text.ElideRight
maximumLineCount: 1
font.weight: Font.Medium
}
StatusBaseText {
Layout.leftMargin: -parent.spacing/2
text: "<b>·</b> %1".arg(qsTr("%n asset(s)", "", communityDelegateRoot.childCount))
elide: Text.ElideRight
color: Theme.palette.baseColor1
maximumLineCount: 1
visible: !d.communityGroupsExpanded
}
Item { Layout.fillWidth: true }
ManageTokenMenuButton {
currentIndex: visualIndex
count: d.controller.communityTokenGroupsModel.count
isGroup: true
groupId: model.communityId
onMoveRequested: (from, to) => d.controller.communityTokenGroupsModel.moveItem(from, to)
onShowHideGroupRequested: (groupId, flag) => d.controller.showHideGroup(groupId, flag)
}
}
StatusListView {
Layout.fillWidth: true
Layout.preferredHeight: contentHeight
model: d.controller.communityTokensModel
interactive: false
visible: d.communityGroupsExpanded
displaced: Transition {
NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad }
}
delegate: LocalTokenDelegate {
isGrouped: true
count: communityDelegateRoot.childCount
dragEnabled: count > 1
keys: ["x-status-draggable-community-token-item-%1".arg(model.communityId)]
bgColor: Theme.palette.indirectColor4
topInset: 2 // tighter "spacing"
bottomInset: 2
visible: communityDelegateRoot.communityId === model.communityId
}
}
}
}
}
contentItem: ColumnLayout {
spacing: Style.current.padding
StatusListView {
Layout.fillWidth: true
model: d.controller.regularTokensModel
implicitHeight: contentHeight
interactive: false
displaced: Transition {
NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad }
}
delegate: LocalTokenDelegate {
count: d.controller.regularTokensModel.count
dragEnabled: count > 1
keys: ["x-status-draggable-token-item"]
}
}
RowLayout {
id: communityTokensHeader
Layout.fillWidth: true
Layout.topMargin: Style.current.padding
visible: d.controller.communityTokensModel.count
StatusBaseText {
color: Theme.palette.baseColor1
text: qsTr("Community")// + " -> %1".arg(switchArrangeByCommunity.checked ? d.controller.communityTokenGroupsModel.count : d.controller.communityTokensModel.count)
}
Item { Layout.fillWidth: true }
StatusSwitch {
LayoutMirroring.enabled: true
LayoutMirroring.childrenInherit: true
id: switchArrangeByCommunity
textColor: Theme.palette.baseColor1
text: qsTr("Arrange by community")
}
}
StatusModalDivider {
Layout.fillWidth: true
Layout.topMargin: -Style.current.halfPadding
visible: communityTokensHeader.visible && switchArrangeByCommunity.checked
}
StatusLinkText {
Layout.alignment: Qt.AlignTrailing
visible: communityTokensHeader.visible && switchArrangeByCommunity.checked
text: d.communityGroupsExpanded ? qsTr("Collapse all") : qsTr("Expand all")
normalColor: linkColor
font.weight: Font.Normal
onClicked: d.communityGroupsExpanded = !d.communityGroupsExpanded
}
Loader {
Layout.fillWidth: true
active: d.controller.communityTokensModel.count
visible: active
sourceComponent: switchArrangeByCommunity.checked ? cmpCommunityTokenGroups : cmpCommunityTokens
}
StatusBaseText {
Layout.fillWidth: true
Layout.topMargin: Style.current.padding
color: Theme.palette.baseColor1
text: qsTr("Hidden")// + " -> %1".arg(d.controller.hiddenTokensModel.count)
visible: d.controller.hiddenTokensModel.count
}
StatusListView {
Layout.fillWidth: true
model: d.controller.hiddenTokensModel
implicitHeight: contentHeight
interactive: false
displaced: Transition {
NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad }
}
delegate: LocalTokenDelegate {
dragEnabled: false
keys: ["x-status-draggable-none"]
isHidden: true
}
}
}
Component {
id: cmpCommunityTokens
StatusListView {
model: d.controller.communityTokensModel
implicitHeight: contentHeight
interactive: false
displaced: Transition {
NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad }
}
delegate: LocalTokenDelegate {
count: d.controller.communityTokensModel.count
dragEnabled: count > 1
keys: ["x-status-draggable-community-token-item"]
}
}
}
Component {
id: cmpCommunityTokenGroups
StatusListView {
model: d.controller.communityTokenGroupsModel
implicitHeight: contentHeight
interactive: false
spacing: Style.current.halfPadding
displaced: Transition {
NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad }
}
delegate: LocalTokenGroupDelegate {}
}
}
}