feat: Implement the new Link Preview cards

Implementing the new design for the following preview types:
 - Community and channel
 - General link previews (Youtube, github etc)
 - Image link preview

The storybook implementation has all these links available for testing.
Missing features in the app:
 - Logo (favicon)
 - Community card
 - Image preview details (title, domain name)
This commit is contained in:
Alex Jbanca 2023-09-13 12:12:47 +03:00 committed by Alex Jbanca
parent 2ac484dd57
commit 7b6281a6c6
9 changed files with 509 additions and 192 deletions

View File

@ -0,0 +1,204 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core.Theme 0.1
import shared.controls.chat 1.0
import utils 1.0
SplitView {
id: root
property alias logoSettings: previewCard.logoSettings
property string ytBannerQuality: "hqdefault"
Pane {
SplitView.fillWidth: true
SplitView.fillHeight: true
LinkPreviewCard {
id: previewCard
bannerImageSource: "https://img.youtube.com/vi/yHN1M7vcPKU/%1.jpg".arg(root.ytBannerQuality)
title: titleInput.text
description: descriptionInput.text
footer: footerInput.text
logoSettings.name: Style.png("tokens/SOCKS")
logoSettings.isImage: true
logoSettings.isLetterIdenticon: false
}
}
Pane {
SplitView.preferredWidth: 500
SplitView.fillHeight: true
ColumnLayout {
spacing: 24
ColumnLayout {
Label {
text: "Title"
}
TextField {
id: titleInput
Layout.fillHeight: true
Layout.fillWidth: true
text: "What Is Web3? A Decentralized Internet Via Blockchain Technology That Will Revolutionise All Sectors- Decrypt (@decryptmedia) August 31 2021"
}
Label {
text: "Description"
}
RowLayout {
TextField {
id: descriptionInput
Layout.fillHeight: true
Layout.fillWidth: true
text: "Link description goes here. If blank it will enable multi line title."
}
Button {
text: "clear"
onClicked: descriptionInput.text = ""
}
Button {
text: "Set"
onClicked: descriptionInput.text = "Link description goes here. If blank it will enable multi line title."
}
}
Label {
text: "Footer"
}
TextField {
id: footerInput
Layout.fillHeight: true
Layout.fillWidth: true
text: footerTypeCommunity.footerRichText
}
}
ColumnLayout {
Label {
Layout.fillWidth: true
text: "Logo"
}
RadioButton {
text: qsTr("No logo")
checked: root.logoSettings.name === "" && root.logoSettings.emoji === ""
onToggled: {
root.logoSettings.name = ""
root.logoSettings.emoji = ""
root.logoSettings.isImage = false
root.logoSettings.isLetterIdenticon = false
}
}
RadioButton {
readonly property string rawImageData: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB0AAAAcCAYAAACdz7SqAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAM2SURBVHgBtVbNbtNAEJ7ZpBQ4pRGF9kQqWqkBRNwnwLlxI9y4NX2CiiOntE9QeINw49a8QdwT3NhKQCKaSj4WUVXmABRqe5hxE+PGTuyk5ZOSXe/ftzs/3y5CBiw/NEzw/cdAaCJAifgXdCA4QGAjggbEvbMf0LJt7aSth6lkHjW4akIG8GI2/1k5H7e7XW2PGRdHqWQU8jdoNytZIrnC7YNPupnUnxtuWF01SjhD77hqwPQosNlrxdt34OTb172xpELoKvrA1QW4EqCZRJyLEnpI7ZBQggThlGvXYVLI3HAeE88vfj85Pno/6FaDiqeoEUZlMA9bvc/7cxyxVa6/SeM5j2Tcdn/hnHsNly520s7KAyN0V17+7pWNGhHVhxYJTNLraosLi8e0kMBxT0FH00IW830oeT/ButBertjRQ5BPO1xUQ1IE2oQUHHZ0K6mdI1RzoSEdpqRg76O2lPgSElKDdz919JYMoxA95QDow7qUykWoxTo5z2YIXsGUsLV2CPD1cDu7MODiQKKnsVmI1jhFyQJvFrb6URxFQWJAYYIZSEF6tKZATitFQpehEm1PkCraWYCE+8Nt5ENBwX8EAd2NNaKQxu0ukVuCqwATQHwnjhphShMuiSAVKZ527E6bzYt78Q3SulxvcAm44K8ntXMqagmkJDUpzNwMZGsqBDqLuDXcLvkvqajcWWgm+ZUI6svlym5fsbITlh9tsgi0Ezs5//vkMtBocqSJOZw84ZrHPiXFJ6UwECx5A/FbqNXX2hAiefkzqCNRha1Wi8yJgddeCk4qHzkK1aMgdypfshYRbkTGm3z0Rs6LW0REgDXVEMuMI0TE5kDlgkv8+PjIKRYXfzPxEyH2EYzDzv7L4q1FHsvpg8Gkt186OlGp5uYXZMjzkYS8txwfQnj63//APmzDIF1yWJVrCDJgeZVfjTjCj0KicC3qlny0053FZ/k/PFnyy6P2yv1Kk1T/1eCGF/pEYCncGI6DCzIo/uGnRvg8CfzE5MEPoQGT4Pz5Uj3oxp+hMe0V4oOOrssOMfmWyMJo5X1cG2WZkYIvO2Tn85sGXwg5B5Q9kiKMas5DntPr6Oq4+/gvs8hkkbAzoC8AAAAASUVORK5CYII="
text: qsTr("Raw image")
checked: root.logoSettings.name === rawImageData
onToggled: {
root.logoSettings.name = rawImageData
root.logoSettings.isImage = true
root.logoSettings.isLetterIdenticon = false
}
}
RadioButton {
text: qsTr("QRC asset: SOCKS")
checked: root.logoSettings.name = Style.png("tokens/SOCKS")
onToggled:{
root.logoSettings.name = Style.png("tokens/SOCKS")
root.logoSettings.isImage = true
root.logoSettings.isLetterIdenticon = false
}
}
RadioButton {
text: qsTr("Letter identicon")
checked: root.logoSettings.name = "github.com"
onToggled: {
root.logoSettings.name = "github.com"
root.logoSettings.emoji = ""
root.logoSettings.isLetterIdenticon = true
root.logoSettings.color = "blue"
}
}
RadioButton {
text: qsTr("Emoji")
checked: root.logoSettings.emoji === "👋"
onToggled: {
root.logoSettings.emoji = "👋"
root.logoSettings.isLetterIdenticon = true
root.logoSettings.color = "orchid"
}
}
}
ColumnLayout {
Label {
Layout.fillWidth: true
text: "Banner size"
}
RadioButton {
text: qsTr("Low (120x90)")
checked: ytBannerQuality === "default"
onToggled: ytBannerQuality = "default"
}
RadioButton {
text: qsTr("Medium(320x180)")
checked: ytBannerQuality === "mqdefault"
onToggled: ytBannerQuality = "mqdefault"
}
RadioButton {
text: qsTr("High(480x360)")
checked: ytBannerQuality === "hqdefault"
onToggled: ytBannerQuality = "hqdefault"
}
}
ColumnLayout {
Label {
Layout.fillWidth: true
text: "Footer type"
}
RadioButton {
id: footerTypeCommunity
property string footerRichText: `<img src="%1" width="16" height="16" style="vertical-align: top" ></img><font color="%2"> 629.2K </font> <img src="%3" width="16" height="16" style="vertical-align: top" ><font color="%2">112.1K</font>`.arg(Style.svg("group")).arg(Theme.palette.directColor1).arg(Style.svg("active-members"))
text: qsTr("Community")
checked: footerInput.text === footerRichText
onToggled: footerInput.text = footerRichText
}
RadioButton {
property string footerRichText: `%1 <img src="%2" width="16" height="16" style="vertical-align: top" ><font color="%2"> %3</font>`.arg(qsTr("Channel in")).arg(Style.png("tokens/SOCKS")).arg(qsTr("Doodles"))
text: qsTr("Channel")
checked: footerInput.text === footerRichText
onToggled: footerInput.text = footerRichText
}
RadioButton {
text: qsTr("Link domain")
property string footerText: "X"
checked: footerInput.text === footerText
onToggled: footerInput.text = footerText
}
}
ColumnLayout {
Label {
Layout.fillWidth: true
text: "Tail position"
}
RadioButton {
text: qsTr("Left")
checked: previewCard.leftTail === true
onToggled: previewCard.leftTail = true
}
RadioButton {
text: qsTr("Right")
checked: previewCard.leftTail === false
onToggled: previewCard.leftTail = false
}
}
}
}
}

View File

@ -0,0 +1,97 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import shared.views.chat 1.0
SplitView {
Pane {
id: messageViewWrapper
SplitView.fillWidth: true
SplitView.fillHeight: true
LinksMessageView {
id: linksMessageView
anchors.fill: parent
store: {}
messageStore: {}
linkPreviewModel: ListModel {
id: linkPreviewModel
ListElement {
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
unfurled: true
hostname: "www.youtube.com"
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
description: "PSY - I LUV IT M/V @ https://youtu.be/Xvjnoagk6GU PSY - New Face M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
linkType: 0 // 0 = link, 1 = image
thumbnailWidth: 480
thumbnailHeight: 360
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
}
ListElement {
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
unfurled: true
hostname: "www.youtube.com"
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
description: "PSY - I LUV IT M/V @ https://youtu.be/Xvjnoagk6GU PSY - New Face M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
linkType: 0 // 0 = link, 1 = image
thumbnailWidth: 480
thumbnailHeight: 360
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
}
ListElement {
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
unfurled: true
hostname: "www.youtube.com"
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
description: "PSY - I LUV IT M/V @ https://youtu.be/Xvjnoagk6GU PSY - New Face M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
linkType: 0 // 0 = link, 1 = image
thumbnailWidth: 480
thumbnailHeight: 360
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
}
ListElement {
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
unfurled: true
hostname: "www.youtube.com"
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
description: "PSY - I LUV IT M/V @ https://youtu.be/Xvjnoagk6GU PSY - New Face M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
linkType: 0 // 0 = link, 1 = image
thumbnailWidth: 480
thumbnailHeight: 360
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
}
}
localUnfurlLinks: {}
isCurrentUser: true
onImageClicked: {
console.log("image clicked")
}
}
}
Pane {
SplitView.minimumWidth: 300
SplitView.preferredWidth: 300
ColumnLayout {
spacing: 25
ColumnLayout {
Label {
text: qsTr("Sender")
}
CheckBox {
text: qsTr("Current user")
checked: linksMessageView.isCurrentUser
onToggled: linksMessageView.isCurrentUser = !linksMessageView.isCurrentUser
}
}
}
}
}

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.46069 13.6656C6.42831 13.957 6.78989 14.1174 6.98417 13.8977L12.6016 7.54762C12.8588 7.25683 12.6524 6.79857 12.2641 6.79857H9.09442C8.91541 6.79857 8.77613 6.64297 8.7959 6.46505L9.25486 2.33445C9.28724 2.04302 8.92565 1.88265 8.73137 2.10227L3.11397 8.45238C2.85673 8.74317 3.06318 9.20143 3.45142 9.20143H6.28554C6.64358 9.20143 6.92212 9.51264 6.88258 9.86848L6.46069 13.6656Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 552 B

View File

@ -0,0 +1,34 @@
import QtQuick 2.13
import QtQuick.Controls 2.15
import utils 1.0
import shared 1.0
Control {
id: root
property bool leftTail: true
property real borderWidth: 1
readonly property Component clippingEffect: CalloutOpacityMask {
width: parent.width
height: parent.height
leftTail: root.leftTail
}
background: Rectangle {
color: Style.current.border
layer.enabled: true
layer.effect: root.clippingEffect
Rectangle {
id: clipping
anchors.fill: parent
anchors.margins: root.borderWidth
color: Style.current.background
layer.enabled: true
layer.effect: root.clippingEffect
}
}
}

View File

@ -0,0 +1,43 @@
import QtQuick 2.15
import QtGraphicalEffects 1.15
import utils 1.0
OpacityMask {
id: root
property bool leftTail: true
readonly property int smallCorner: Style.current.radius / 2
readonly property int bigCorner: Style.current.radius * 2
readonly property int fakeCornerSize: bigCorner * 2
cached: true
maskSource: Item {
width: root.width
height: root.height
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
height: parent.height
radius: root.bigCorner
}
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
width: root.fakeCornerSize
height: root.fakeCornerSize
radius: root.smallCorner
visible: root.leftTail
}
Rectangle {
anchors.bottom: parent.bottom
anchors.right: parent.right
width: root.fakeCornerSize
height: root.fakeCornerSize
radius: root.smallCorner
visible: !root.leftTail
}
}
}

View File

@ -0,0 +1,104 @@
import QtQuick 2.15
import QtQuick.Layouts 1.15
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import shared.status 1.0
CalloutCard {
id: root
/// The title of the callout card
required property string title
required property string description
required property string footer
property StatusAssetSettings logoSettings: StatusAssetSettings {
width: 28
height: 28
bgRadius: bgWidth / 2
}
property string bannerImageSource: ""
signal clicked()
borderWidth: 1
padding: borderWidth
implicitHeight: 290
implicitWidth: 305
contentItem: ColumnLayout {
StatusImage {
id: bannerImage
Layout.fillWidth: true
Layout.preferredHeight: 170
asynchronous: true
source: root.bannerImageSource
fillMode: Image.PreserveAspectCrop
layer.enabled: true
layer.effect: root.clippingEffect
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.topMargin: 12
Layout.bottomMargin: 12
Layout.leftMargin: 12
Layout.rightMargin: 12
RowLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumHeight: root.description.length ? 28 : 72
StatusSmartIdenticon {
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
Layout.preferredWidth: 28
Layout.preferredHeight: 28
asset: root.logoSettings
name: root.logoSettings.name
visible: !!root.logoSettings.name.length || !!root.logoSettings.emoji.length
}
StatusBaseText {
text: root.title
Layout.fillWidth: true
Layout.fillHeight: true
font.pixelSize: 13
font.weight: Font.Medium
wrapMode: Text.Wrap
elide: Text.ElideRight
verticalAlignment: Text.AlignVCenter
}
}
StatusBaseText {
Layout.fillWidth: true
Layout.fillHeight: true
text: root.description
font.pixelSize: 12
wrapMode: Text.Wrap
elide: Text.ElideRight
color: Theme.palette.baseColor1
visible: root.description.length
}
StatusBaseText {
id: linkSite
Layout.fillWidth: true
text: root.footer
font.pixelSize: 12
lineHeight: 16
lineHeightMode: Text.FixedHeight
color: Theme.palette.baseColor1
elide: Text.ElideRight
verticalAlignment: Text.AlignBottom
textFormat: Text.RichText
}
}
}
MouseArea {
anchors.fill: root
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: root.clicked()
}
}

View File

@ -1,88 +0,0 @@
import QtQuick 2.13
import QtGraphicalEffects 1.13
import QtQuick.Layouts 1.13
import utils 1.0
import shared 1.0
Item {
id: root
property bool isCurrentUser: false
readonly property int smallCorner: Style.current.radius / 2
readonly property int bigCorner: Style.current.radius * 2
readonly property int fakeCornerSize: bigCorner * 2
Rectangle {
width: parent.width + 2
height: parent.height + 2
anchors.top: parent.top
anchors.left: parent.left
anchors.topMargin: -1
anchors.leftMargin: -1
radius: root.bigCorner
border.width: 2
border.color: Style.current.border
}
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.bottomMargin: -1
anchors.leftMargin: -1
width: root.fakeCornerSize
height: root.fakeCornerSize
radius: root.smallCorner
visible: !root.isCurrentUser
border.width: 2
border.color: Style.current.border
}
Rectangle {
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.bottomMargin: -1
anchors.rightMargin: -1
width: root.fakeCornerSize
height: root.fakeCornerSize
radius: root.smallCorner
visible: root.isCurrentUser
border.width: 2
border.color: Style.current.border
}
Rectangle {
anchors.fill: parent
color: Style.current.background
layer.enabled: true
layer.effect: OpacityMask {
maskSource: Item {
width: root.width
height: root.height
Rectangle {
anchors.top: parent.top
anchors.left: parent.left
width: parent.width
height: parent.height
radius: root.bigCorner
}
Rectangle {
anchors.bottom: parent.bottom
anchors.left: parent.left
width: root.fakeCornerSize
height: root.fakeCornerSize
radius: root.smallCorner
visible: !root.isCurrentUser
}
Rectangle {
anchors.bottom: parent.bottom
anchors.right: parent.right
width: root.fakeCornerSize
height: root.fakeCornerSize
radius: root.smallCorner
visible: root.isCurrentUser
}
}
}
}
}

View File

@ -1,5 +1,7 @@
CalloutCard 1.0 CalloutCard.qml
FetchMoreMessagesButton 1.0 FetchMoreMessagesButton.qml
GapComponent 1.0 GapComponent.qml
LinkPreviewCard 1.0 LinkPreviewCard.qml
UsernameLabel 1.0 UsernameLabel.qml
DateGroup 1.0 DateGroup.qml
UserImage 1.0 UserImage.qml
@ -10,7 +12,6 @@ Retry 1.0 Retry.qml
SendTransactionButton 1.0 SendTransactionButton.qml
StateBubble 1.0 StateBubble.qml
GasSelectorButton 1.0 GasSelectorButton.qml
MessageBorder 1.0 MessageBorder.qml
EmojiReaction 1.0 EmojiReaction.qml
ProfileHeader 1.0 ProfileHeader.qml
VerificationLabel 1.0 VerificationLabel.qml

View File

@ -16,13 +16,13 @@ import shared.controls.chat 1.0
ColumnLayout {
id: root
property var store
property var messageStore
required property var store
required property var messageStore
property var linkPreviewModel
property var localUnfurlLinks
required property var linkPreviewModel
required property var localUnfurlLinks
property bool isCurrentUser: false
required property bool isCurrentUser
signal imageClicked(var image, var mouse, var imageSource, string url)
@ -47,40 +47,31 @@ ColumnLayout {
property bool animated: false
asynchronous: true
active: unfurled && hostname != ""
StateGroup {
//Using StateGroup as a warkardound for https://bugreports.qt.io/browse/QTBUG-47796
states: [
State {
name: "loadLinkPreview"
when: linkMessageLoader.linkType === Constants.LinkPreviewType.Link
PropertyChanges { target: linkMessageLoader; sourceComponent: unfurledLinkComponent }
},
State {
name: "loadImage"
when: linkMessageLoader.linkType === Constants.LinkPreviewType.Image
PropertyChanges { target: linkMessageLoader; sourceComponent: unfurledImageComponent }
}
// NOTE: New unfurling not yet suppport status links.
// Uncomment code below when implemented:
// - https://github.com/status-im/status-go/issues/3762
// State {
// name: "statusInvitation"
// when: linkMessageLoader.isStatusDeepLink
// PropertyChanges { target: linkMessageLoader; sourceComponent: invitationBubble }
// }
]
sourceComponent: LinkPreviewCard {
id: unfurledLink
leftTail: !root.isCurrentUser
bannerImageSource: thumbnailUrl
title: parent.title
description: parent.description
footer: hostname
onClicked: {
Global.openLink(url)
}
}
}
}
//TODO: Remove this once we have gif support in new unfurling flow
Component {
id: unfurledImageComponent
MessageBorder {
CalloutCard {
implicitWidth: linkImage.width
implicitHeight: linkImage.height
isCurrentUser: root.isCurrentUser
leftTail: !root.isCurrentUser
StatusChatImageLoader {
id: linkImage
@ -138,6 +129,8 @@ ColumnLayout {
}
}
// Code below can be dropped when New unfurling flow suppports GIFs.
Component {
id: invitationBubble
@ -166,78 +159,6 @@ ColumnLayout {
}
}
Component {
id: unfurledLinkComponent
MessageBorder {
id: unfurledLink
implicitWidth: linkImage.visible ? linkImage.width + 2 : 300
implicitHeight: {
if (linkImage.visible) {
return linkImage.height + (Style.current.smallPadding * 2) + (linkTitle.height + 2 + linkSite.height)
}
return (Style.current.smallPadding * 2) + linkTitle.height + 2 + linkSite.height
}
isCurrentUser: root.isCurrentUser
StatusChatImageLoader {
id: linkImage
objectName: "LinksMessageView_unfurledLinkComponent_linkImage"
source: thumbnailUrl
visible: thumbnailUrl.length
imageWidth: Math.min(300, thumbnailWidth > 0 ? thumbnailWidth : 300)
isCurrentUser: root.isCurrentUser
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
isOnline: root.store.mainModuleInst.isOnline
asynchronous: true
onClicked: {
Global.openLink(url)
}
}
StatusBaseText {
id: linkTitle
text: title
font.pixelSize: 13
font.weight: Font.Medium
wrapMode: Text.Wrap
anchors.top: linkImage.visible ? linkImage.bottom : parent.top
anchors.topMargin: Style.current.smallPadding
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: Style.current.smallPadding
anchors.rightMargin: Style.current.smallPadding
}
StatusBaseText {
id: linkSite
text: hostname
font.pixelSize: 12
font.weight: Font.Thin
color: Theme.palette.baseColor1
anchors.top: linkTitle.bottom
anchors.topMargin: 2
anchors.left: linkTitle.left
anchors.bottomMargin: Style.current.halfPadding
}
MouseArea {
anchors.top: linkImage.visible ? linkImage.top : linkTitle.top
anchors.left: linkImage.visible ? linkImage.left : linkTitle.left
anchors.right: linkImage.visible ? linkImage.right : linkTitle.right
anchors.bottom: linkSite.bottom
cursorShape: Qt.PointingHandCursor
onClicked: {
Global.openLink(url)
}
}
}
}
// Code below can be dropped when New unfurling flow suppports GIFs.
QtObject {
id: d
@ -454,6 +375,4 @@ ColumnLayout {
}
}
}