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

333 lines
12 KiB
QML
Raw Permalink Normal View History

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Popups 0.1
import shared.controls 1.0
import utils 1.0
import AppLayouts.Profile.controls 1.0
Control {
id: root
property var baseModel
property var showcaseModel
readonly property var showcaseRoles: ["showcaseVisibility", "order"]
// to override
property string keyRole
property var roleNames: []
property var filterFunc: (modelData) => true
property string hiddenPlaceholderBanner
property string showcasePlaceholderBanner
property Component draggableDelegateComponent
property Component showcaseDraggableDelegateComponent
signal showcaseEntryChanged()
function reset() {
showcaseModel.clear()
updateBaseModelFilters()
}
function updateBaseModelFilters() {
// Reset base model to update filter conditions
hiddenItemsListView.model = null
hiddenItemsListView.model = baseModel
}
readonly property Connections showcaseUpdateConnections: Connections {
target: showcaseModel
function onBaseModelFilterConditionsMayHaveChanged() {
root.updateBaseModelFilters()
}
}
background: null
component VisibilityDropArea: AbstractButton {
id: visibilityDropAreaLocal
property int showcaseVisibility: Constants.ShowcaseVisibility.NoOne
readonly property alias containsDrag: dropArea.containsDrag
padding: Style.current.halfPadding
spacing: padding/2
icon.color: Theme.palette.primaryColor1
background: ShapeRectangle {
path.strokeColor: dropArea.containsDrag ? "transparent" : Theme.palette.directColor7
path.fillColor: dropArea.containsDrag ? Theme.palette.white : "transparent"
DropArea {
id: dropArea
anchors.fill: parent
keys: ["x-status-draggable-showcase-item-hidden"]
onEntered: function(drag) {
drag.accept()
}
onDropped: function(drop) {
var showcaseObj = drop.source.showcaseObj
var tmpObj = Object()
root.roleNames.forEach(role => tmpObj[role] = showcaseObj[role])
tmpObj.showcaseVisibility = visibilityDropAreaLocal.showcaseVisibility
showcaseModel.upsertItemJson(JSON.stringify(tmpObj))
root.showcaseEntryChanged()
}
}
}
contentItem: Item {
RowLayout {
width: Math.min(parent.width, implicitWidth)
anchors.centerIn: parent
spacing: visibilityDropAreaLocal.spacing
StatusIcon {
width: 20
height: 20
icon: ProfileUtils.visibilityIcon(visibilityDropAreaLocal.showcaseVisibility)
color: visibilityDropAreaLocal.icon.color
}
StatusBaseText {
Layout.fillWidth: true
font.pixelSize: 13
font.weight: Font.Medium
elide: Text.ElideRight
color: visibilityDropAreaLocal.icon.color
text: visibilityDropAreaLocal.text
}
}
}
}
QtObject {
id: d
readonly property int defaultDelegateHeight: 60
readonly property int contentSpacing: 12
readonly property int strokeMargin: 2
}
contentItem: ColumnLayout {
spacing: d.contentSpacing
StatusBaseText {
Layout.fillWidth: true
text: qsTr("In showcase")
color: Theme.palette.baseColor1
font.pixelSize: 13
font.weight: Font.Medium
}
StatusListView {
id: showcaseItemsListView
Layout.fillWidth: true
Layout.minimumHeight: Math.floor(targetDropArea.height + targetDropArea.anchors.bottomMargin)
model: showcaseModel
implicitHeight: contentHeight
interactive: false
displaced: Transition {
NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad }
}
delegate: DropArea {
id: showcaseDelegateRoot
property int visualIndex: index
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 }
}
width: ListView.view.width
height: visible && showcaseDraggableDelegateLoader.item ? showcaseDraggableDelegateLoader.item.height : 0
keys: ["x-status-draggable-showcase-item"]
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()
showcaseModel.move(from, to)
drag.accept()
}
Loader {
id: showcaseDraggableDelegateLoader
width: parent.width
sourceComponent: root.showcaseDraggableDelegateComponent
property var modelData: model
property var dragParentData: root
property int visualIndexData: index
}
}
// overlaid at the bottom of the listview
DropArea {
id: targetDropArea
width: parent.width
height: d.defaultDelegateHeight
anchors.bottom: parent.bottom
anchors.bottomMargin: showcaseModel.count ? Style.current.halfPadding : 0
keys: ["x-status-draggable-showcase-item-hidden"]
ShapeRectangle {
anchors.fill: parent
anchors.margins: d.strokeMargin
visible: !showcaseModel.count && !showcaseCombinedDropArea.visible
text: root.hiddenPlaceholderBanner
}
Rectangle {
id: showcaseCombinedDropArea
width: parent.width
height: parent.height + d.strokeMargin
anchors.centerIn: parent
color: Theme.palette.baseColor5
radius: Style.current.radius
visible: parent.containsDrag || dropAreaEveryone.containsDrag || dropAreaContacts.containsDrag || dropAreaVerified.containsDrag
RowLayout {
width: parent.width - spacing*2
anchors.centerIn: parent
spacing: d.contentSpacing
VisibilityDropArea {
id: dropAreaEveryone
Layout.fillWidth: true
showcaseVisibility: Constants.ShowcaseVisibility.Everyone
text: qsTr("Everyone")
}
VisibilityDropArea {
id: dropAreaContacts
Layout.fillWidth: true
showcaseVisibility: Constants.ShowcaseVisibility.Contacts
text: qsTr("Contacts")
}
VisibilityDropArea {
id: dropAreaVerified
Layout.fillWidth: true
showcaseVisibility: Constants.ShowcaseVisibility.IdVerifiedContacts
text: qsTr("Verified")
}
}
}
}
}
StatusBaseText {
Layout.fillWidth: true
Layout.topMargin: Style.current.halfPadding
text: qsTr("Hidden")
color: Theme.palette.baseColor1
font.pixelSize: 13
font.weight: Font.Medium
}
StatusListView {
id: hiddenItemsListView
Layout.fillWidth: true
Layout.minimumHeight: empty ? Math.floor(hiddenTargetDropArea.height + hiddenTargetDropArea.anchors.topMargin)
: d.defaultDelegateHeight * Math.min(count, 4)
model: root.baseModel
readonly property bool empty: !contentHeight
displaced: Transition {
NumberAnimation { properties: "x,y"; easing.type: Easing.OutQuad }
}
delegate: DropArea {
id: delegateRoot
property int visualIndex: index
visible: root.filterFunc(model)
width: ListView.view.width
height: visible && draggableDelegateLoader.item ? draggableDelegateLoader.item.height : 0
keys: ["x-status-draggable-showcase-item"]
onEntered: function(drag) {
drag.accept()
}
onDropped: function(drop) {
showcaseModel.setVisibilityByIndex(drop.source.visualIndex, Constants.ShowcaseVisibility.NoOne)
root.showcaseEntryChanged()
}
Rectangle {
width: parent.width
height: d.defaultDelegateHeight
anchors.centerIn: parent
color: Theme.palette.baseColor5
radius: Style.current.radius
visible: draggableDelegateLoader.item && draggableDelegateLoader.item.dragActive
}
Loader {
id: draggableDelegateLoader
width: parent.width
sourceComponent: root.draggableDelegateComponent
property var modelData: model
property var dragParentData: root
property int visualIndexData: delegateRoot.visualIndex
}
}
// overlaid at the top of the listview
DropArea {
id: hiddenTargetDropArea
width: parent.width
height: d.defaultDelegateHeight
anchors.top: parent.top
anchors.topMargin: !hiddenItemsListView.empty ? Style.current.halfPadding : 0
keys: ["x-status-draggable-showcase-item"]
ShapeRectangle {
readonly property bool stroked: hiddenItemsListView.empty && !parent.containsDrag
anchors.fill: parent
anchors.margins: d.strokeMargin
visible: hiddenItemsListView.empty || parent.containsDrag
path.fillColor: stroked ? "transparent" : Theme.palette.baseColor5
path.strokeColor: stroked ? Theme.palette.baseColor2 : "transparent"
text: root.showcasePlaceholderBanner
}
onEntered: function(drag) {
drag.accept()
}
onDropped: function(drop) {
showcaseModel.setVisibilityByIndex(drop.source.visualIndex, Constants.ShowcaseVisibility.NoOne)
root.showcaseEntryChanged()
root.updateBaseModelFilters()
}
}
}
}
}