status-desktop/ui/imports/shared/controls/chat/ChatInputLinksPreviewArea.qml
Alex Jbanca 4a30d13bdc
feat(LinkPreviews): Integrate Link previews with the backend (#12523)
* feat(StatusQ): Adding numberToLocaleStringInCompactForm function to LocaleUtils

This function will format the number in a compact form
E.g: 1000 -> 1K; 1000000 -> 1M; 1100000 -> 1.1M

+ adding tests

fix(statusQ): Update numberToLocaleStringInCompactForm to return the locale number when greter than 999T

fix(StatusQ): extend the test_numberToLocaleStringInCompactForm with new data

* feat(LinkPreviews): Update the link preview area in StatusChatInput to use the new model

Changes:
1. Create a new component `LinkPreviewMiniCardDelegate.qml` that filters the model data to properly fill the link preview card with the needed data based on the preview type
2. Update storybook pages
3. Small updates to LinkPreviewMiniCard

* feat(LinkPreviews): Update the link previews in message history to use the new backend

Changes:
1. Create delegate items for LinkPreviewCard and gif link preview to clean the LinksMessageView component and filter the model data based on the preview type
2. Remove UserProfileCard and reuse the LinkPreviewCard to display contacts link previews
3. Update LinkPreviewCard so that it can accommodate status link previews (communities, channels, contacts). The generic properties (title, description, footer) have been dropped and replaced with specialised properties for each preview type.
4. Fix LinkPreviewCard layout to better accommodate different content variants (missing properties, long/short title, missing description, missing icon)
5. Fixing the link preview context menu and click actions

fix: Move inline components to separate files

Fixing the linux builds using Qt 5.15.2 affected by this bug:
https://bugreports.qt.io/browse/QTBUG-89180

* fix: Align LinkPreviewMiniCard implementation with LinkPreviewCard and remove state based model filtering
2023-10-25 18:20:02 +03:00

176 lines
5.6 KiB
QML

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.delegates 1.0
import utils 1.0
import SortFilterProxyModel 0.2
Control {
id: root
required property var imagePreviewArray
/*
Expected roles:
string title
string url
bool unfurled
bool immutable
string hostname
string description
int linkType
int thumbnailWidth
int thumbnailHeight
string thumbnailUrl
string thumbnailDataUri
*/
required property var linkPreviewModel
required property bool showLinkPreviewSettings
readonly property alias hoveredUrl: d.hoveredUrl
readonly property bool hasContent: imagePreviewArray.length > 0 || showLinkPreviewSettings || linkPreviewRepeater.count > 0
signal imageRemoved(int index)
signal imageClicked(var chatImage)
signal linkReload(string link)
signal linkClicked(string link)
signal enableLinkPreview()
signal enableLinkPreviewForThisMessage()
signal disableLinkPreview()
signal dismissLinkPreviewSettings()
signal dismissLinkPreview(int index)
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
onFlickStarted: settingsContextMenu.close()
RowLayout {
id: layout
spacing: 8
StatusChatInputImageArea {
id: imageArea
Layout.preferredHeight: 64
spacing: layout.spacing
imageSource: imagePreviewArray
onImageClicked: root.imageClicked(chatImage)
onImageRemoved: root.imageRemoved(index)
visible: !!imagePreviewArray && imagePreviewArray.length > 0
}
Repeater {
id: linkPreviewRepeater
model: d.filteredModel
delegate: LinkPreviewMiniCardDelegate {
required property int index
onClose: root.dismissLinkPreview(d.filteredModel.mapToSource(index))
onRetry: root.linkReload(url)
onClicked: root.linkClicked(url)
onRightClicked: settingsContextMenu.popup()
onContainsMouseChanged: {
if (containsMouse) {
d.hoveredUrl = url
} else if (d.hoveredUrl === url) {
d.hoveredUrl = ""
}
}
Component.onDestruction: {
if(d.hoveredUrl === url) {
d.hoveredUrl = ""
}
}
}
}
LinkPreviewSettingsCard {
id: settingsCard
visible: root.showLinkPreviewSettings
onDismiss: root.dismissLinkPreviewSettings()
onEnableLinkPreviewForThisMessage: root.enableLinkPreviewForThisMessage()
onEnableLinkPreview: root.enableLinkPreview()
onDisableLinkPreview: root.disableLinkPreview()
}
}
}
}
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
}
]
}
}
LinkPreviewSettingsCardMenu {
id: settingsContextMenu
onEnableLinkPreviewForThisMessage: root.enableLinkPreviewForThisMessage()
onEnableLinkPreview: root.enableLinkPreview()
onDisableLinkPreview: root.disableLinkPreview()
}
}