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

297 lines
11 KiB
QML

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.controls 1.0
import shared.status 1.0
import utils 1.0
import "./private"
CalloutCard {
id: root
readonly property LinkData linkData: LinkData { }
readonly property UserData userData: UserData { }
readonly property CommunityData communityData: CommunityData { }
readonly property ChannelData channelData: ChannelData { }
property int type: Constants.LinkPreviewType.NoPreview
property bool highlight: false
signal clicked(var mouse)
borderWidth: 1
implicitHeight: 290
implicitWidth: 324+2*borderWidth
hoverEnabled: true
dropShadow: d.highlight
borderColor: d.highlight ? Style.current.background : Style.current.border
Behavior on borderColor {
ColorAnimation { duration: 200 }
}
contentItem: ColumnLayout {
Loader {
id: bannerImageLoader
Layout.fillWidth: true
Layout.leftMargin: d.bannerImageMargins
Layout.rightMargin: d.bannerImageMargins
Layout.topMargin: d.bannerImageMargins
Layout.preferredHeight: 170
Layout.preferredWidth: 324
active: !!d.bannerImageSource
sourceComponent: StatusImage {
id: bannerImage
asynchronous: true
source: d.bannerImageSource
fillMode: Image.PreserveAspectCrop
layer.enabled: true
layer.effect: root.clippingEffect
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.margins: 12
Loader {
id: userImageLoader
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
Layout.topMargin: 8
Layout.bottomMargin: 8
visible: active
active: root.type === Constants.LinkPreviewType.StatusContact
sourceComponent: UserImage {
interactive: false
imageWidth: 58
imageHeight: imageWidth
ensVerified: root.userData.ensVerified
name: root.userData.name
pubkey: root.userData.publicKey
image: root.userData.image
}
}
RowLayout {
id: titleLayout
Layout.fillWidth: true
Layout.fillHeight: true
Layout.maximumHeight: description.text.length ? 28 : 72
Layout.minimumHeight: 18
StatusSmartIdenticon {
id: logo
Layout.alignment: Qt.AlignTop
Layout.preferredWidth: 28
Layout.preferredHeight: 28
asset.width: width
asset.height: height
visible: false
}
StatusBaseText {
id: title
objectName: "linkPreviewTitle"
// One line centered next to the logo
// Two or more lines, or no logo, top aligned
readonly property bool centerText: lineCount == 1 && height === logo.height && logo.visible
Layout.fillWidth: true
Layout.fillHeight: true
Layout.alignment: Qt.AlignTop
Layout.topMargin: verticalAlignment === Text.AlignTop && contentHeight < logo.height ? (logo.height - contentHeight) / 2 : 0
font.pixelSize: 13
font.weight: Font.Medium
wrapMode: Text.Wrap
elide: Text.ElideRight
verticalAlignment: centerText ? Text.AlignVCenter : Text.AlignTop
visible: text.length
}
}
EmojiHash {
Layout.topMargin: 4
Layout.bottomMargin: 6
visible: root.type === Constants.LinkPreviewType.StatusContact
publicKey: root.userData.publicKey
oneRow: true
objectName: "linkPreviewEmojiHash"
}
StatusBaseText {
id: description
Layout.fillWidth: true
Layout.fillHeight: true
font.pixelSize: 12
wrapMode: Text.Wrap
elide: Text.ElideRight
color: Theme.palette.baseColor1
visible: description.text.length
}
Loader {
id: footerLoader
Layout.fillWidth: true
visible: active
sourceComponent: FooterText {
}
}
}
}
MouseArea {
anchors.fill: root
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: root.clicked(mouse)
}
component FooterText: StatusBaseText {
font.pixelSize: 12
lineHeight: 16
lineHeightMode: Text.FixedHeight
color: Theme.palette.baseColor1
elide: Text.ElideRight
wrapMode: Text.Wrap
verticalAlignment: Text.AlignBottom
text: root.linkData.domain
maximumLineCount: 1
}
Component {
id: channelFooterComponent
RowLayout {
spacing: 4
FooterText {
Layout.fillHeight: true
text: qsTr("Channel in")
verticalAlignment: Text.AlignVCenter
}
StatusRoundedImage {
Layout.preferredHeight: 16
Layout.preferredWidth: height
image.source: channelData.communityData.image
}
FooterText {
Layout.fillHeight: true
Layout.fillWidth: true
text: channelData.communityData.name
verticalAlignment: Text.AlignVCenter
color: Theme.palette.directColor1
}
}
}
Component {
id: communityFooterComponent
RowLayout {
spacing: 2
StatusIcon {
icon: "group"
color: Theme.palette.directColor1
width: 16
height: width
}
FooterText {
Layout.fillHeight: true
Layout.fillWidth: !communityData.activeMembersCountAvailable
color: Theme.palette.directColor1
text: LocaleUtils.numberToLocaleStringInCompactForm(communityData.membersCount)
verticalAlignment: Text.AlignVCenter
}
StatusIcon {
Layout.leftMargin: 6
icon: "active-members"
color: Theme.palette.directColor1
width: 16
height: width
visible: communityData.activeMembersCountAvailable
}
FooterText {
Layout.fillWidth: true
Layout.fillHeight: true
color: Theme.palette.directColor1
text: LocaleUtils.numberToLocaleStringInCompactForm(communityData.activeMembersCount)
verticalAlignment: Text.AlignVCenter
visible: communityData.activeMembersCountAvailable
}
}
}
//behavior
states: [
State {
name: "noPreview"
when: root.type === Constants.LinkPreviewType.NoPreview
PropertyChanges { target: root; visible: false }
},
State {
name: "linkPreview"
when: root.type === Constants.LinkPreviewType.Standard
PropertyChanges {
target: logo
visible: !!root.linkData.image
name: root.linkData.domain
asset.name: root.linkData.image
asset.isImage: !!root.linkData.image
asset.color: Theme.palette.baseColor2
}
PropertyChanges { target: bannerImageLoader; visible: true }
PropertyChanges { target: title; text: root.linkData.title }
PropertyChanges { target: description; text: root.linkData.description }
PropertyChanges { target: d; bannerImageSource: root.linkData.thumbnail }
},
State {
name: "community"
when: root.type === Constants.LinkPreviewType.StatusCommunity
PropertyChanges {
target: logo
visible: true
name: root.communityData.name
asset.name: root.communityData.image
asset.isImage: !!root.communityData.image
asset.color: root.communityData.color
}
PropertyChanges { target: bannerImageLoader; visible: true }
PropertyChanges { target: title; text: root.communityData.name }
PropertyChanges { target: description; text: root.communityData.description }
PropertyChanges { target: d; bannerImageSource: root.communityData.banner }
PropertyChanges { target: footerLoader; active: true; visible: !root.communityData.encrypted || root.communityData.joined; sourceComponent: communityFooterComponent }
},
State {
name: "channel"
when: root.type === Constants.LinkPreviewType.StatusCommunityChannel
PropertyChanges {
target: logo
visible: true
name: root.channelData.name
asset.name: ""
asset.isImage: false
asset.color: root.channelData.color
asset.emoji: root.channelData.emoji
}
PropertyChanges { target: bannerImageLoader; visible: true }
PropertyChanges { target: title; text: "#" + root.channelData.name }
PropertyChanges { target: description; text: root.channelData.description || root.channelData.communityData.description }
PropertyChanges { target: d; bannerImageSource: root.channelData.communityData.banner }
PropertyChanges { target: footerLoader; active: true; visible: true; sourceComponent: channelFooterComponent }
},
State {
name: "contact"
when: root.type === Constants.LinkPreviewType.StatusContact
PropertyChanges { target: root; implicitHeight: 187 }
PropertyChanges { target: bannerImageLoader; visible: false }
PropertyChanges { target: footerLoader; active: false; visible: !root.userData.bio; Layout.fillHeight: true }
PropertyChanges { target: title; text: root.userData.name }
PropertyChanges { target: description; text: root.userData.bio; Layout.minimumHeight: 32; visible: true }
}
]
QtObject {
id: d
property real bannerImageMargins: 1 / Screen.devicePixelRatio // image size isn't pixel perfect..
property bool highlight: root.highlight || root.hovered
property string bannerImageSource: ""
}
}