From 331f27b52a836e901221e807593b1b832a0163c9 Mon Sep 17 00:00:00 2001 From: Alex Jbanca Date: Mon, 9 Oct 2023 15:41:27 +0300 Subject: [PATCH] feat: highlight the hovered hyperlink and link preview --- .../src/StatusQ/Components/StatusMessage.qml | 7 +++++++ .../statusMessage/StatusTextMessage.qml | 14 ++++++++------ ui/StatusQ/src/StatusQ/Core/Utils/Utils.qml | 1 + .../shared/controls/chat/LinkPreviewCard.qml | 19 +++++++++++++++++++ .../shared/views/chat/LinksMessageView.qml | 9 ++++++++- ui/imports/shared/views/chat/MessageView.qml | 3 +++ 6 files changed, 46 insertions(+), 7 deletions(-) diff --git a/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml b/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml index 29fb1ab625..2c3643b065 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml @@ -65,6 +65,8 @@ Control { property bool profileClickable: true property bool hideMessage: false property bool isInPinnedPopup + property string highlightedLink: "" + property string hoveredLink: "" property StatusMessageDetails messageDetails: StatusMessageDetails {} property StatusMessageDetails replyDetails: StatusMessageDetails {} @@ -273,9 +275,13 @@ Control { isEdited: root.isEdited allowShowMore: !root.isInPinnedPopup textField.anchors.rightMargin: root.isInPinnedPopup ? /*Style.current.xlPadding*/ 32 : 0 // margin for the "Unpin" floating button + highlightedLink: root.highlightedLink onLinkActivated: { root.linkActivated(link); } + textField.onHoveredLinkChanged: { + root.hoveredLink = hoveredLink; + } } } Loader { @@ -295,6 +301,7 @@ Control { messageDetails: root.messageDetails allowShowMore: !root.isInPinnedPopup textField.anchors.rightMargin: root.isInPinnedPopup ? /*Style.current.xlPadding*/ 32 : 0 // margin for the "Unpin" floating button + highlightedLink: root.highlightedLink onLinkActivated: { root.linkActivated(link); } diff --git a/ui/StatusQ/src/StatusQ/Components/private/statusMessage/StatusTextMessage.qml b/ui/StatusQ/src/StatusQ/Components/private/statusMessage/StatusTextMessage.qml index 5e1f6610d1..72e6a99c30 100644 --- a/ui/StatusQ/src/StatusQ/Components/private/statusMessage/StatusTextMessage.qml +++ b/ui/StatusQ/src/StatusQ/Components/private/statusMessage/StatusTextMessage.qml @@ -9,6 +9,10 @@ import StatusQ.Core.Utils 0.1 Item { id: root + readonly property alias hoveredLink: chatText.hoveredLink + + property string highlightedLink: "" + property StatusMessageDetails messageDetails: StatusMessageDetails {} property bool isEdited: false property bool convertToSingleLine: false @@ -24,6 +28,8 @@ Item { QtObject { id: d + property string hoveredLink: chatText.hoveredLink || root.highlightedLink + property bool readMore: false property bool isQuote: false readonly property int showMoreHeight: showMoreButtonLoader.visible ? showMoreButtonLoader.height : 0 @@ -44,7 +50,7 @@ Item { const editedMessage = formattedMessage.slice(0, index) + ` ` + qsTr("(edited)") + `` + formattedMessage.slice(index); - return Utils.getMessageWithStyle(Emoji.parse(editedMessage), chatText.hoveredLink) + return Utils.getMessageWithStyle(Emoji.parse(editedMessage), d.hoveredLink) } if (root.convertToSingleLine || isQuote) @@ -60,7 +66,7 @@ Item { // short return not to add styling when no html return formattedMessage - return Utils.getMessageWithStyle(formattedMessage, chatText.hoveredLink) + return Utils.getMessageWithStyle(formattedMessage, d.hoveredLink) } } @@ -98,10 +104,6 @@ Item { onLinkActivated: { root.linkActivated(link); } - onLinkHovered: { - // Strange thing. Without this empty stub the cursorShape - // is not changed to pointingHandCursor. - } } // Horizontal crop mask diff --git a/ui/StatusQ/src/StatusQ/Core/Utils/Utils.qml b/ui/StatusQ/src/StatusQ/Core/Utils/Utils.qml index 01e2bebe01..57297be7f4 100644 --- a/ui/StatusQ/src/StatusQ/Core/Utils/Utils.qml +++ b/ui/StatusQ/src/StatusQ/Core/Utils/Utils.qml @@ -197,6 +197,7 @@ QtObject { `padding: 0px 2px;` + `}` + (hoveredLink !== "" ? `a.mention[href="${hoveredLink}"] { background-color: ${Theme.palette.mentionColor2}; }` : ``) + + (hoveredLink !== "" ? `a[href="${hoveredLink}"] { background-color: ${Theme.palette.primaryColor3}; }` : ``) + `del {` + `text-decoration: line-through;` + `}` + diff --git a/ui/imports/shared/controls/chat/LinkPreviewCard.qml b/ui/imports/shared/controls/chat/LinkPreviewCard.qml index 93f2984dde..21755e46ec 100644 --- a/ui/imports/shared/controls/chat/LinkPreviewCard.qml +++ b/ui/imports/shared/controls/chat/LinkPreviewCard.qml @@ -1,11 +1,13 @@ import QtQuick 2.15 import QtQuick.Layouts 1.15 +import QtQuick.Window 2.15 import StatusQ.Core 0.1 import StatusQ.Core.Theme 0.1 import StatusQ.Components 0.1 import shared.status 1.0 +import utils 1.0 CalloutCard { id: root @@ -14,6 +16,9 @@ CalloutCard { required property string title required property string description required property string footer + + property bool highlight: false + property StatusAssetSettings logoSettings: StatusAssetSettings { width: 28 height: 28 @@ -27,11 +32,20 @@ CalloutCard { borderWidth: 1 implicitHeight: 290 implicitWidth: 305 + hoverEnabled: true + borderColor: hovered || highlight ? Style.current.borderTertiary : Style.current.border + + Behavior on borderColor { + ColorAnimation { duration: 200 } + } contentItem: ColumnLayout { StatusImage { id: bannerImage Layout.fillWidth: true + Layout.leftMargin: d.bannerImageMargins + Layout.rightMargin: d.bannerImageMargins + Layout.topMargin: d.bannerImageMargins Layout.preferredHeight: 170 asynchronous: true source: root.bannerImageSource @@ -98,4 +112,9 @@ CalloutCard { acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: root.clicked(mouse) } + + QtObject { + id: d + property real bannerImageMargins: 1 / Screen.devicePixelRatio // image size isn't pixel perfect.. + } } diff --git a/ui/imports/shared/views/chat/LinksMessageView.qml b/ui/imports/shared/views/chat/LinksMessageView.qml index 96e73e7231..9300c4efb2 100644 --- a/ui/imports/shared/views/chat/LinksMessageView.qml +++ b/ui/imports/shared/views/chat/LinksMessageView.qml @@ -23,6 +23,9 @@ Flow { required property bool isCurrentUser + readonly property alias hoveredLink: linksRepeater.hoveredUrl + property string highlightLink: "" + signal imageClicked(var image, var mouse, var imageSource, string url) spacing: 12 @@ -80,8 +83,10 @@ Flow { Repeater { id: linksRepeater - model: root.linkPreviewModel + property string hoveredUrl: "" + + model: root.linkPreviewModel delegate: Loader { id: linkMessageLoader @@ -120,6 +125,8 @@ Flow { break } } + highlight: root.highlightLink === url + onHoveredChanged: linksRepeater.hoveredUrl = hovered ? url : "" } } } diff --git a/ui/imports/shared/views/chat/MessageView.qml b/ui/imports/shared/views/chat/MessageView.qml index b936cf9718..3ce22ef5ab 100644 --- a/ui/imports/shared/views/chat/MessageView.qml +++ b/ui/imports/shared/views/chat/MessageView.qml @@ -755,14 +755,17 @@ Loader { linksComponent: Component { LinksMessageView { + id: linksMessageView linkPreviewModel: root.linkPreviewModel localUnfurlLinks: root.localUnfurlLinks messageStore: root.messageStore store: root.rootStore isCurrentUser: root.amISender + highlightLink: delegate.hoveredLink onImageClicked: (image, mouse, imageSource, url) => { d.onImageClicked(image, mouse, imageSource, url) } + onHoveredLinkChanged: delegate.highlightedLink = linksMessageView.hoveredLink } }