status-desktop/ui/app/AppLayouts/Profile/panels/ProfileShowcasePanel.qml

431 lines
15 KiB
QML

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml 2.15
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Core.Utils 0.1
import StatusQ.Core.Theme 0.1
import shared.controls 1.0
import utils 1.0
import AppLayouts.Profile.controls 1.0
DoubleFlickableWithFolding {
id: root
readonly property var showcaseRoles: ["showcaseVisibility", "order"]
required property string keyRole
required property var roleNames
required property var filterFunc
property var baseModel
property var showcaseModel
property Component showcaseDraggableDelegateComponent
property Component hiddenDraggableDelegateComponent
property Component additionalFooterComponent
property string emptyInShowcasePlaceholderText
property string emptyHiddenPlaceholderText
readonly property Connections showcaseUpdateConnections: Connections {
target: root.showcaseModel
function onBaseModelFilterConditionsMayHaveChanged() {
root.updateBaseModelFilters()
}
}
function reset() {
root.showcaseModel.clear()
updateBaseModelFilters()
}
function updateBaseModelFilters() {
// Reset base model to update filter conditions
hiddenListView.model = null
hiddenListView.model = root.baseModel
}
signal showcaseEntryChanged()
QtObject {
id: d
readonly property var dragHiddenItemKey: ["x-status-draggable-showcase-item-hidden"]
readonly property var dragShowcaseItemKey: ["x-status-draggable-showcase-item"]
property bool isAnyShowcaseDragActive: false
property bool isAnyHiddenDragActive: false
property int additionalHeaderComponentWidth: 350 // by design
property int additionalHeaderComponentHeight: 40 // by design
}
clip: true
ScrollBar.vertical: StatusScrollBar {
policy: ScrollBar.AsNeeded
visible: resolveVisibility(policy, root.height, root.contentHeight)
}
flickable1: EmptyShapeRectangleFooterListView {
id: inShowcaseListView
model: root.showcaseModel
width: root.width
placeholderText: root.emptyInShowcasePlaceholderText
footerHeight: ProfileUtils.defaultDelegateHeight
footerContentVisible: !dropAreaRow.visible
spacing: Style.current.halfPadding
header: FoldableHeader {
width: ListView.view.width
title: qsTr("In showcase")
folded: root.flickable1Folded
rightAdditionalComponent: VisibilityDropAreaButtonsRow {
width: d.additionalHeaderComponentWidth
height: d.additionalHeaderComponentHeight
margins: 0
visible: root.flickable1Folded &&
(d.isAnyHiddenDragActive ||
parent.containsDrag ||
everyoneContainsDrag ||
contactsContainsDrag ||
verifiedContainsDrag)
}
onToggleFolding: root.flip1Folding()
}
delegate: DropArea {
id: showcaseDelegateRoot
property int visualIndex: index
width: ListView.view.width
height: visible && showcaseDraggableDelegateLoader.item ? showcaseDraggableDelegateLoader.item.height : 0
keys: d.dragShowcaseItemKey
visible: model.showcaseVisibility !== Constants.ShowcaseVisibility.NoOne
onEntered: function(drag) {
const from = drag.source.visualIndex
const to = showcaseDraggableDelegateLoader.item.visualIndex
if (to === from)
return
root.showcaseEntryChanged()
root.showcaseModel.move(from, to, 1)
drag.accept()
}
// TODO:
// This animation is causing issues when there are no elements in the showcase list.
// Reenable it once the refactor of the models and delegates is done (simplified): #13498
// ListView.onRemove: SequentialAnimation {
// PropertyAction { target: showcaseDelegateRoot; property: "ListView.delayRemove"; value: true }
// NumberAnimation { target: showcaseDelegateRoot; property: "scale"; to: 0; easing.type: Easing.InOutQuad }
// PropertyAction { target: showcaseDelegateRoot; property: "ListView.delayRemove"; value: false }
// }
// In showcase delegate item container:
Loader {
id: showcaseDraggableDelegateLoader
property var modelData: model
property var dragParentData: root
property int visualIndexData: index
width: parent.width
sourceComponent: root.showcaseDraggableDelegateComponent
}
// Delegate shadow background when dragging:
ShadowDelegate {
id: showcaseShadow
visible: showcaseDraggableDelegateLoader.item && showcaseDraggableDelegateLoader.item.dragActive
onVisibleChanged: d.isAnyShowcaseDragActive = visible
}
Binding {
when: dropAreaRow.visible
target: showcaseDraggableDelegateLoader.item
property: "blurState"
value: true
restoreMode: Binding.RestoreBindingOrValue
}
}
// Overlaid showcase listview content drop area:
DropArea {
anchors.bottom: parent.bottom
width: parent.width
height: parent.contentHeight
keys: d.dragHiddenItemKey
// Shown at the bottom of the listview
VisibilityDropAreaButtonsRow {
id: dropAreaRow
width: parent.width
height: ProfileUtils.defaultDelegateHeight
anchors.bottom: parent.bottom
visible: d.isAnyHiddenDragActive ||
parent.containsDrag ||
everyoneContainsDrag ||
contactsContainsDrag ||
verifiedContainsDrag
}
}
}
flickable2: EmptyShapeRectangleFooterListView {
id: hiddenListView
model: root.baseModel
width: root.width
placeholderText: root.emptyHiddenPlaceholderText
footerHeight: ProfileUtils.defaultDelegateHeight
footerContentVisible: !hiddenDropAreaButton.visible
empty: root.showcaseModel.hiddenCount === 0 && !root.flickable2Folded // TO BE REMOVE: #13498
additionalFooterComponent: root.additionalFooterComponent
spacing: Style.current.halfPadding
header: FoldableHeader {
width: ListView.view.width
title: qsTr("Hidden")
folded: root.flickable2Folded
rightAdditionalComponent: VisibilityDropAreaButton {
visible: root.flickable2Folded && (d.isAnyShowcaseDragActive || parent.containsDrag || containsDrag)
width: d.additionalHeaderComponentWidth
height: d.additionalHeaderComponentHeight
rightInset: 1
text: qsTr("Hide")
dropAreaKeys: d.dragShowcaseItemKey
onDropped: {
root.showcaseModel.setVisibilityByIndex(drop.source.visualIndex, visibility)
root.showcaseEntryChanged()
}
}
onToggleFolding: root.flip2Folding()
}
delegate: DropArea {
id: hiddenDelegateRoot
property int visualIndex: index
visible: root.filterFunc(model)
width: ListView.view.width
height: visible && hiddenDraggableDelegateLoader.item ? hiddenDraggableDelegateLoader.item.height : 0
keys: d.dragShowcaseItemKey
onEntered: function(drag) {
drag.accept()
}
onDropped: function(drop) {
root.showcaseModel.setVisibilityByIndex(drop.source.visualIndex, Constants.ShowcaseVisibility.NoOne)
root.showcaseEntryChanged()
}
// Hidden delegate item container:
Loader {
id: hiddenDraggableDelegateLoader
property var modelData: model
property var dragParentData: root
property int visualIndexData: hiddenDelegateRoot.visualIndex
width: parent.width
sourceComponent: root.hiddenDraggableDelegateComponent
}
// Delegate shadow background when dragging:
ShadowDelegate {
id: hiddenShadow
visible: hiddenDraggableDelegateLoader.item && hiddenDraggableDelegateLoader.item.dragActive
onVisibleChanged: d.isAnyHiddenDragActive = visible
}
Binding {
when: hiddenDropAreaButton.visible
target: hiddenDraggableDelegateLoader.item
property: "blurState"
value: true
restoreMode: Binding.RestoreBindingOrValue
}
}
// Overlaid hidden listview content drop area:
DropArea {
anchors.top: parent.top
width: parent.width
height: parent.contentHeight
keys: d.dragShowcaseItemKey
// Shown at the top of the listview
VisibilityDropAreaButton {
id: hiddenDropAreaButton
anchors.top: parent.top
anchors.topMargin: hiddenListView.headerItem.height + Style.current.padding
anchors.horizontalCenter: parent.horizontalCenter
visible: d.isAnyShowcaseDragActive || parent.containsDrag || hiddenDropAreaButton.containsDrag
width: parent.width - Style.current.padding
height: ProfileUtils.defaultDelegateHeight - Style.current.padding
text: qsTr("Hide")
dropAreaKeys: d.dragShowcaseItemKey
onDropped: {
root.showcaseModel.setVisibilityByIndex(drop.source.visualIndex, visibility)
root.showcaseEntryChanged()
root.updateBaseModelFilters()
}
}
}
}
component VisibilityDropAreaButton: AbstractButton {
id: visibilityDropAreaButton
readonly property alias containsDrag: dropArea.containsDrag
property int showcaseVisibility: Constants.ShowcaseVisibility.NoOne
property var dropAreaKeys
signal dropped(var drop, int visibility)
padding: Style.current.halfPadding
spacing: padding/2
icon.color: Theme.palette.primaryColor1
background: ShapeRectangle {
path.strokeColor: dropArea.containsDrag ? Theme.palette.primaryColor2 : Theme.palette.directColor7
path.fillColor: dropArea.containsDrag ? Theme.palette.primaryColor3 : Theme.palette.baseColor4
DropArea {
id: dropArea
anchors.fill: parent
keys: visibilityDropAreaButton.dropAreaKeys
onEntered: function(drag) {
drag.accept()
}
onDropped: function(drop) {
visibilityDropAreaButton.dropped(drop, visibilityDropAreaButton.showcaseVisibility)
}
}
}
contentItem: Item {
RowLayout {
width: Math.min(parent.width, implicitWidth)
anchors.centerIn: parent
spacing: visibilityDropAreaButton.spacing
StatusIcon {
width: 20
height: width
icon: ProfileUtils.visibilityIcon(visibilityDropAreaButton.showcaseVisibility)
color: visibilityDropAreaButton.icon.color
}
StatusBaseText {
Layout.fillWidth: true
font.pixelSize: Style.current.additionalTextSize
font.weight: Font.Medium
elide: Text.ElideRight
color: visibilityDropAreaButton.icon.color
text: visibilityDropAreaButton.text
}
}
}
}
component VisibilityDropAreaButtonsRow: Item {
id: visibilityDropAreaRow
readonly property bool everyoneContainsDrag: dropAreaEveryone.containsDrag
readonly property bool contactsContainsDrag: dropAreaContacts.containsDrag
readonly property bool verifiedContainsDrag: dropAreaVerified.containsDrag
property int margins: Style.current.halfPadding
function dropped(drop, visibility) {
var showcaseObj = drop.source.showcaseObj
// need to set total balance for an asset
if (drop.source.totalValue !== undefined) {
showcaseObj.enabledNetworkBalance = drop.source.totalValue
}
var tmpObj = Object()
root.roleNames.forEach(role => tmpObj[role] = showcaseObj[role])
tmpObj.showcaseVisibility = visibility
root.showcaseModel.upsertItemJson(JSON.stringify(tmpObj))
root.showcaseEntryChanged()
}
RowLayout {
anchors.fill: parent
anchors.margins: visibilityDropAreaRow.margins
spacing: Style.current.halfPadding
VisibilityDropAreaButton {
id: dropAreaEveryone
Layout.fillWidth: true
Layout.fillHeight: true
showcaseVisibility: Constants.ShowcaseVisibility.Everyone
text: qsTr("Everyone")
dropAreaKeys: d.dragHiddenItemKey
onDropped: visibilityDropAreaRow.dropped(drop, visibility)
}
VisibilityDropAreaButton {
id: dropAreaContacts
Layout.fillWidth: true
Layout.fillHeight: true
showcaseVisibility: Constants.ShowcaseVisibility.Contacts
text: qsTr("Contacts")
dropAreaKeys: d.dragHiddenItemKey
onDropped: visibilityDropAreaRow.dropped(drop, visibility)
}
VisibilityDropAreaButton {
id: dropAreaVerified
Layout.fillWidth: true
Layout.fillHeight: true
showcaseVisibility: Constants.ShowcaseVisibility.IdVerifiedContacts
text: qsTr("Verified")
dropAreaKeys: d.dragHiddenItemKey
onDropped: visibilityDropAreaRow.dropped(drop, visibility)
}
}
}
component ShadowDelegate: Rectangle {
width: parent.width
height: ProfileUtils.defaultDelegateHeight
anchors.centerIn: parent
color: Theme.palette.baseColor5
radius: Style.current.radius
}
}