status-desktop/ui/imports/shared/controls/chat/ChatInputLinksPreviewArea.qml

161 lines
5.8 KiB
QML
Raw Normal View History

import QtQuick 2.15
import QtQuick.Controls 2.15
import QtGraphicalEffects 1.15
import QtQuick.Layouts 1.15
import StatusQ.Core 0.1
import shared.status 1.0
import shared.controls.chat 1.0
import SortFilterProxyModel 0.2
Control {
id: root
required property var imagePreviewModel
required property var linkPreviewModel
readonly property alias hoveredUrl: d.hoveredUrl
readonly property int contentItemsCount: imagePreviewModel.length + d.filteredModel.count
signal imageRemoved(int index)
signal imageClicked(var chatImage)
signal linkReload(string link)
signal linkClicked(string link)
signal linkRemoved(string link)
horizontalPadding: 12
topPadding: 12
contentItem: Item {
id: opacityMaskWrapper
anchors.fill: parent
implicitWidth: flickable.implicitWidth
implicitHeight: flickable.implicitHeight
opacity: 0
WheelHandler {
target: flickable
property: "contentX"
acceptedDevices: PointerDevice.Mouse
onActiveChanged: if(!active) flickable.returnToBounds()
}
Flickable {
id: flickable
anchors.fill: parent
anchors.leftMargin: root.leftPadding
anchors.rightMargin: root.rightPadding
anchors.bottomMargin: root.bottomPadding
anchors.topMargin: root.topPadding
implicitHeight: contentHeight
implicitWidth: contentWidth
contentWidth: layout.width
contentHeight: layout.height
RowLayout {
id: layout
spacing: 8
StatusChatInputImageArea {
id: imageArea
Layout.preferredHeight: 64
spacing: layout.spacing
imageSource: imagePreviewModel
onImageClicked: root.imageClicked(chatImage)
onImageRemoved: root.imageRemoved(index)
visible: !!imagePreviewModel && imagePreviewModel.length > 0
}
Repeater {
model: d.filteredModel
delegate: LinkPreviewMiniCard {
// Model properties
required property string title
required property string url
required property bool unfurled
required property bool immutable
required property string hostname
required property string description
required property int linkType
required property int thumbnailWidth
required property int thumbnailHeight
required property string thumbnailUrl
required property string thumbnailDataUri
required property int index
Layout.preferredHeight: 64
titleStr: title
domain: hostname //TODO: use domain when available
favIconUrl: "" //TODO: use favicon when available
communityName: "" //TODO: add community info when available
channelName: "" //TODO: add community info when available
thumbnailImageUrl: thumbnailUrl.length > 0 ? thumbnailUrl : thumbnailDataUri
type: linkType === 0 ? LinkPreviewMiniCard.Type.Link : LinkPreviewMiniCard.Type.Image
previewState: unfurled && hostname != "" ? LinkPreviewMiniCard.State.Loaded :
unfurled && hostname === "" ? LinkPreviewMiniCard.State.LoadingFailed :
!unfurled ? LinkPreviewMiniCard.State.Loading : LinkPreviewMiniCard.State.Invalid
onClose: root.linkPreviewModel.removePreviewData(d.filteredModel.mapToSource(index))
onRetry: root.linkReload(url)
onClicked: root.linkClicked(url)
onContainsMouseChanged: {
if (containsMouse) {
d.hoveredUrl = url
} else if (d.hoveredUrl === url) {
d.hoveredUrl = ""
}
}
Component.onDestruction: {
if(d.hoveredUrl === url) {
d.hoveredUrl = ""
}
}
}
}
}
}
}
Rectangle {
id: horizontalClipMask
anchors.fill: opacityMaskWrapper
visible: false
gradient: Gradient {
orientation: Gradient.Horizontal
GradientStop { position: 0.0; color: "transparent" }
GradientStop { position: root.horizontalPadding / horizontalClipMask.width; color: "white" }
GradientStop { position: 1 - root.horizontalPadding / horizontalClipMask.width; color: "white" }
GradientStop { position: 1; color: "transparent" }
}
}
OpacityMask {
anchors.fill: opacityMaskWrapper
source: opacityMaskWrapper
maskSource: horizontalClipMask
}
QtObject {
id: d
property string hoveredUrl: ""
property SortFilterProxyModel filteredModel: SortFilterProxyModel {
id: filteredModel
sourceModel: root.linkPreviewModel
filters: [
ExpressionFilter {
expression: { return !model.immutable || model.unfurled } // Filter out immutable links that haven't been unfurled yet
}
]
}
}
}