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
This commit is contained in:
parent
5c4dd60f1e
commit
4a30d13bdc
|
@ -4,6 +4,7 @@ import QtQuick.Layouts 1.15
|
|||
import QtGraphicalEffects 1.15
|
||||
|
||||
import Storybook 1.0
|
||||
import Models 1.0
|
||||
|
||||
import StatusQ.Core.Theme 0.1
|
||||
import shared.controls.chat 1.0
|
||||
|
@ -30,7 +31,7 @@ SplitView {
|
|||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
imagePreviewArray: ["https://picsum.photos/200/300?random=1", "https://picsum.photos/200/300?random=1"]
|
||||
linkPreviewModel: showLinkPreviewSettings ? emptyModel : linkPreviewListModel
|
||||
linkPreviewModel: showLinkPreviewSettings ? emptyModel : mockedLinkPreviewModel
|
||||
showLinkPreviewSettings: !linkPreviewEnabledSwitch.checked
|
||||
visible: hasContent
|
||||
|
||||
|
@ -78,123 +79,8 @@ SplitView {
|
|||
id: emptyModel
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: linkPreviewListModel
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
immutable: false
|
||||
hostname: "youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: ""
|
||||
linkType: 0
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://picsum.photos/480/360?random=1"
|
||||
thumbnailDataUri: ""
|
||||
}
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: false
|
||||
immutable: false
|
||||
hostname: "youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: ""
|
||||
linkType: 0
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://picsum.photos/480/360?random=2"
|
||||
thumbnailDataUri: ""
|
||||
}
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
immutable: false
|
||||
hostname: ""
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: ""
|
||||
linkType: 0
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://picsum.photos/480/360?random=3"
|
||||
thumbnailDataUri: ""
|
||||
}
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
hostname: "youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: ""
|
||||
linkType: 0
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://picsum.photos/480/360?random=4"
|
||||
thumbnailDataUri: ""
|
||||
}
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
immutable: false
|
||||
hostname: "youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: ""
|
||||
linkType: 0
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://picsum.photos/480/360?random=5"
|
||||
thumbnailDataUri: ""
|
||||
}
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
hostname: "youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: ""
|
||||
linkType: 0
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://picsum.photos/480/360?random=6"
|
||||
thumbnailDataUri: ""
|
||||
}
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
immutable: false
|
||||
hostname: "youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: ""
|
||||
linkType: 0
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://picsum.photos/480/360?random=7"
|
||||
thumbnailDataUri: ""
|
||||
}
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
immutable: false
|
||||
hostname: "youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: ""
|
||||
linkType: 0
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://picsum.photos/480/360?random=8"
|
||||
thumbnailDataUri: ""
|
||||
}
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
immutable: false
|
||||
hostname: "youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: ""
|
||||
linkType: 0
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://picsum.photos/480/360?random=9"
|
||||
thumbnailDataUri: ""
|
||||
}
|
||||
LinkPreviewModel {
|
||||
id: mockedLinkPreviewModel
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,16 +2,48 @@ import QtQuick 2.15
|
|||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import Qt.labs.settings 1.0
|
||||
|
||||
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"
|
||||
property string image: Style.png("tokens/SOCKS")
|
||||
property string banner: rawImageCheck.checked ? rawImageCheck.rawImageData : "https://img.youtube.com/vi/yHN1M7vcPKU/%1.jpg".arg(root.ytBannerQuality)
|
||||
property bool globalUtilsReady: false
|
||||
|
||||
// globalUtilsInst mock
|
||||
QtObject {
|
||||
function getEmojiHashAsJson(publicKey) {
|
||||
return JSON.stringify(["👨🏻🍼", "🏃🏿♂️", "🌇", "🤶🏿", "🏮","🤷🏻♂️", "🤦🏻", "📣", "🤎", "👷🏽", "😺", "🥞", "🔃", "🧝🏽♂️"])
|
||||
}
|
||||
function getColorId(publicKey) { return 4 }
|
||||
|
||||
function getCompressedPk(publicKey) { return "zx3sh" + publicKey }
|
||||
|
||||
function getColorHashAsJson(publicKey) {
|
||||
return JSON.stringify([{4: 0, segmentLength: 1},
|
||||
{5: 19, segmentLength: 2}])
|
||||
}
|
||||
|
||||
function isCompressedPubKey(publicKey) { return true }
|
||||
|
||||
Component.onCompleted: {
|
||||
Utils.globalUtilsInst = this
|
||||
root.globalUtilsReady = true
|
||||
|
||||
}
|
||||
Component.onDestruction: {
|
||||
root.globalUtilsReady = false
|
||||
Utils.globalUtilsInst = {}
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
SplitView.fillWidth: true
|
||||
|
@ -19,22 +51,96 @@ SplitView {
|
|||
|
||||
LinkPreviewCard {
|
||||
id: previewCard
|
||||
bannerImageSource: "https://img.youtube.com/vi/yHN1M7vcPKU/%1.jpg".arg(root.ytBannerQuality)
|
||||
type: 1
|
||||
linkData {
|
||||
title: titleInput.text
|
||||
description: descriptionInput.text
|
||||
footer: footerInput.text
|
||||
logoSettings.name: Style.png("tokens/SOCKS")
|
||||
logoSettings.isImage: true
|
||||
logoSettings.isLetterIdenticon: false
|
||||
domain: footerInput.text
|
||||
thumbnail: root.banner
|
||||
image: root.image
|
||||
}
|
||||
userData {
|
||||
name: userNameInput.text
|
||||
publicKey: "zQ3shgmVJjmwwhkfAemjDizYJtv9nzot7QD4iRJ52ZkgdU6Ci"
|
||||
bio: bioInput.text
|
||||
image: root.image
|
||||
ensVerified: false
|
||||
}
|
||||
communityData {
|
||||
name: titleInput.text
|
||||
description: descriptionInput.text
|
||||
banner: root.banner
|
||||
image: root.image
|
||||
membersCount: parseInt(membersCountInput.text)
|
||||
activeMembersCount: parseInt(activeMembersCountInput.text)
|
||||
color: "orchid"
|
||||
}
|
||||
channelData {
|
||||
name: titleInput.text
|
||||
description: descriptionInput.text
|
||||
emoji: ""
|
||||
color: "blue"
|
||||
communityData {
|
||||
name: "Community" + titleInput.text
|
||||
description: "Community" + descriptionInput.text
|
||||
banner: root.banner
|
||||
image: root.image
|
||||
membersCount: parseInt(membersCountInput.text)
|
||||
activeMembersCount: parseInt(activeMembersCountInput.text)
|
||||
color: "orchid"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
|
||||
ScrollView {
|
||||
SplitView.preferredWidth: 500
|
||||
SplitView.fillHeight: true
|
||||
leftPadding: 10
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
spacing: 24
|
||||
ColumnLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Card type"
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
text: qsTr("Link")
|
||||
checked: previewCard.type === Constants.LinkPreviewType.Standard
|
||||
onToggled: previewCard.type = Constants.LinkPreviewType.Standard
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
text: qsTr("Contact")
|
||||
checked: previewCard.type === Constants.LinkPreviewType.StatusContact
|
||||
onToggled: previewCard.type = Constants.LinkPreviewType.StatusContact
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
text: qsTr("Community")
|
||||
checked: previewCard.type === Constants.LinkPreviewType.StatusCommunity
|
||||
onToggled: {
|
||||
previewCard.type = Constants.LinkPreviewType.StatusCommunity
|
||||
titleInput.text = "Socks"
|
||||
descriptionInput.text = "Community description goes here. If blank it will enable multi line title."
|
||||
}
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
text: qsTr("Channel")
|
||||
checked: previewCard.type === Constants.LinkPreviewType.StatusCommunityChannel
|
||||
onToggled: {
|
||||
previewCard.type = Constants.LinkPreviewType.StatusCommunityChannel
|
||||
titleInput.text = "general"
|
||||
descriptionInput.text = "Channel description goes here. If blank it will enable multi line title."
|
||||
}
|
||||
}
|
||||
}
|
||||
ColumnLayout {
|
||||
visible: previewCard.type !== Constants.LinkPreviewType.StatusContact
|
||||
Label {
|
||||
text: "Title"
|
||||
}
|
||||
|
@ -65,7 +171,8 @@ SplitView {
|
|||
onClicked: descriptionInput.text = "Link description goes here. If blank it will enable multi line title."
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: previewCard.type === Constants.LinkPreviewType.Standard
|
||||
Label {
|
||||
text: "Footer"
|
||||
}
|
||||
|
@ -74,68 +181,115 @@ SplitView {
|
|||
id: footerInput
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
text: footerTypeCommunity.footerRichText
|
||||
text: "X"
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: previewCard.type === Constants.LinkPreviewType.StatusCommunity
|
||||
Label {
|
||||
text: "MembersCount"
|
||||
}
|
||||
TextField {
|
||||
id: membersCountInput
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
text: "629200"
|
||||
}
|
||||
TextField {
|
||||
id: activeMembersCountInput
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
text: "112100"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: previewCard.type === Constants.LinkPreviewType.StatusContact
|
||||
Label {
|
||||
text: "User name"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: userNameInput
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
text: "Test user name"
|
||||
}
|
||||
|
||||
Label {
|
||||
text: "Bio"
|
||||
}
|
||||
RowLayout {
|
||||
TextField {
|
||||
id: bioInput
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
text: "User bio description goes here. If blank it will enable multi line title."
|
||||
}
|
||||
Button {
|
||||
text: "clear"
|
||||
onClicked: bioInput.text = ""
|
||||
}
|
||||
Button {
|
||||
text: "Set"
|
||||
onClicked: bioInput.text = "User bio description goes here. If blank it will enable multi line title."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: previewCard.type === Constants.LinkPreviewType.StatusCommunityChannel
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: "channel settings"
|
||||
}
|
||||
CheckBox {
|
||||
text: qsTr("Emoji")
|
||||
checked: previewCard.channelData.emoji === "👋"
|
||||
onToggled: previewCard.channelData.emoji = checked ? "👋" : ""
|
||||
}
|
||||
RadioButton {
|
||||
text: qsTr("Blue channel color")
|
||||
checked: previewCard.channelData.color === "blue"
|
||||
onToggled: previewCard.channelData.color = "blue"
|
||||
}
|
||||
RadioButton {
|
||||
text: qsTr("Red channel color")
|
||||
checked: previewCard.channelData.color === "red"
|
||||
onToggled: previewCard.channelData.color = "red"
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
text: qsTr("no image")
|
||||
checked: root.image === ""
|
||||
onToggled: root.image = ""
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
checked: root.image === rawImageData
|
||||
onToggled: root.image = rawImageData
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
checked: root.image === Style.png("tokens/SOCKS")
|
||||
onToggled: root.image = Style.png("tokens/SOCKS")
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: previewCard.type !== Constants.LinkPreviewType.StatusContact
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Banner size"
|
||||
|
@ -157,29 +311,17 @@ SplitView {
|
|||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: rawImageCheck
|
||||
readonly property string rawImageData: "data:image/jpeg;base64,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"
|
||||
text: qsTr("Raw image banner")
|
||||
checked: root.banner === rawImageData
|
||||
}
|
||||
|
||||
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
|
||||
text: "UserName"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -201,4 +343,12 @@ SplitView {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
Settings {
|
||||
property alias linkType: previewCard.type
|
||||
}
|
||||
}
|
||||
|
||||
// category: Controls
|
||||
|
||||
// https://www.figma.com/file/Mr3rqxxgKJ2zMQ06UAKiWL/💬-Chat⎜Desktop?type=design&node-id=22347-219545&mode=design&t=bODv5MUGQgU9ThJF-0
|
||||
|
|
|
@ -25,14 +25,38 @@ SplitView {
|
|||
LinkPreviewMiniCard {
|
||||
id: previewMiniCard
|
||||
anchors.centerIn: parent
|
||||
titleStr: type === LinkPreviewMiniCard.Type.User ? userNameInput.text : titleInput.text
|
||||
domain: domainInput.text
|
||||
favIconUrl: faviconInput.text
|
||||
previewState: stateInput.currentIndex
|
||||
thumbnailImageUrl: externalImageInput.text
|
||||
type: previewTypeInput.currentIndex
|
||||
communityName: communityNameInput.text
|
||||
channelName: channelNameInput.text
|
||||
previewState: stateInput.currentIndex
|
||||
linkData {
|
||||
title: titleInput.text
|
||||
description: ""
|
||||
domain: domainInput.text
|
||||
thumbnail: externalImageInput.text
|
||||
image: faviconInput.text
|
||||
}
|
||||
userData {
|
||||
name: userNameInput.text
|
||||
publicKey: "zQ3shgmVJjmwwhkfAemjDizYJtv9nzot7QD4iRJ52ZkgdU6Ci"
|
||||
image: faviconInput.text
|
||||
ensVerified: false
|
||||
}
|
||||
communityData {
|
||||
name: communityNameInput.text
|
||||
banner: externalImageInput.text
|
||||
image: faviconInput.text
|
||||
color: "orchid"
|
||||
}
|
||||
channelData {
|
||||
name: channelNameInput.text
|
||||
emoji: ""
|
||||
color: "blue"
|
||||
communityData {
|
||||
name: communityNameInput.text
|
||||
banner: externalImageInput.text
|
||||
image: faviconInput.text
|
||||
color: "orchid"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,7 +74,7 @@ SplitView {
|
|||
id: previewTypeInput
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
model: ["link", "image", "community", "channel", "user profile"]
|
||||
model: ["unknown", "standard", "user profile", "community", "channel"]
|
||||
}
|
||||
Label {
|
||||
text: "Community name"
|
||||
|
|
|
@ -2,87 +2,15 @@ import QtQuick 2.15
|
|||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import Models 1.0
|
||||
|
||||
import shared.views.chat 1.0
|
||||
import shared.stores 1.0
|
||||
|
||||
SplitView {
|
||||
|
||||
ListModel {
|
||||
LinkPreviewModel {
|
||||
id: mockedLinkPreviewModel
|
||||
|
||||
// Create the model dynamically, because `ListElement` doesnt suppport nested elements
|
||||
Component.onCompleted: {
|
||||
const emptyObject = {
|
||||
"unfurled": true,
|
||||
"immutable": false,
|
||||
"empty": false,
|
||||
"url": "",
|
||||
"previewType": 1,
|
||||
"standardPreview": {},
|
||||
"standardPreviewThumbnail": {},
|
||||
"statusContactPreview": {},
|
||||
"statusContactPreviewThumbnail": {},
|
||||
"statusCommunityPreview": {},
|
||||
"statusCommunityPreviewIcon": {},
|
||||
"statusCommunityPreviewBanner": {},
|
||||
"statusCommunityChannelPreview": {},
|
||||
"statusCommunityChannelCommunityPreview": {},
|
||||
"statusCommunityChannelCommunityPreviewIcon": {},
|
||||
"statusCommunityChannelCommunityPreviewBanner": {},
|
||||
}
|
||||
|
||||
const preview1 = Object.assign({}, emptyObject)
|
||||
preview1.url = "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
preview1.previewType = 1
|
||||
preview1.standardPreview = {}
|
||||
preview1.standardPreviewThumbnail = {}
|
||||
preview1.standardPreview.hostname = "www.youtube.com"
|
||||
preview1.standardPreview.title = "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
preview1.standardPreview.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..."
|
||||
preview1.standardPreview.linkType = 0
|
||||
preview1.standardPreviewThumbnail.width = 480
|
||||
preview1.standardPreviewThumbnail.height = 360
|
||||
preview1.standardPreviewThumbnail.url = "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
preview1.standardPreviewThumbnail.dataUri = ""
|
||||
|
||||
|
||||
const preview2 = Object.assign({}, emptyObject)
|
||||
preview2.url = "https://status.app/u/Ow==#zQ3shgmVJjmwwhkfAemjDizYJtv9nzot7QD4iRJ52ZkgdU6Ci"
|
||||
preview2.previewType = 2
|
||||
preview2.statusContactPreview = {}
|
||||
preview2.statusContactPreview.publicKey = "zQ3shgmVJjmwwhkfAemjDizYJtv9nzot7QD4iRJ52ZkgdU6Ci"
|
||||
preview2.statusContactPreview.displayName = "Test contact display name"
|
||||
preview2.statusContactPreview.description = "Test description"
|
||||
preview2.statusContactPreviewThumbnail = {}
|
||||
preview2.statusContactPreviewThumbnail.width = 64
|
||||
preview2.statusContactPreviewThumbnail.height = 64
|
||||
preview2.statusContactPreviewThumbnail.url = "https://placehold.co/64x64"
|
||||
preview2.statusContactPreviewThumbnail.dataUri = ""
|
||||
|
||||
const preview3 = Object.assign({}, emptyObject)
|
||||
preview3.url = "https://status.app/c/ixiACjAKDlRlc3QgQ29tbXVuaXR5Eg9PcGVuIGZvciBhbnlvbmUYdiIHI0ZGMDAwMCoCHwkD#zQ3shnd55dNx9yTihuL6XMbmyM6UNjzU6jk77h5Js31jxcT5V"
|
||||
preview3.previewType = 3
|
||||
preview3.statusCommunityPreview = {}
|
||||
preview3.statusCommunityPreview.communityId = "zQ3shnd55dNx9yTihuL6XMbmyM6UNjzU6jk77h5Js31jxcT5V"
|
||||
preview3.statusCommunityPreview.displayName = "Test community display name"
|
||||
preview3.statusCommunityPreview.description = "Test community description"
|
||||
preview3.statusCommunityPreview.membersCount = 10
|
||||
preview3.statusCommunityPreview.color = "#123456"
|
||||
preview3.statusCommunityPreviewIcon = {}
|
||||
preview3.statusCommunityPreviewIcon.width = 64
|
||||
preview3.statusCommunityPreviewIcon.height = 64
|
||||
preview3.statusCommunityPreviewIcon.url = "https://placehold.co/64x64"
|
||||
preview3.statusCommunityPreviewIcon.dataUri = ""
|
||||
preview3.statusCommunityPreviewBanner = {}
|
||||
preview3.statusCommunityPreviewBanner.width = 320
|
||||
preview3.statusCommunityPreviewBanner.height = 180
|
||||
preview3.statusCommunityPreviewBanner.url = "https://placehold.co/320x180"
|
||||
preview3.statusCommunityPreviewBanner.dataUri = ""
|
||||
|
||||
mockedLinkPreviewModel.append(preview1)
|
||||
mockedLinkPreviewModel.append(preview2)
|
||||
mockedLinkPreviewModel.append(preview3)
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
|
@ -90,38 +18,19 @@ SplitView {
|
|||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
|
||||
component LinkPreviewObject: QtObject {
|
||||
required property string url
|
||||
required property bool unfurled
|
||||
required property bool empty
|
||||
required property int previewType
|
||||
}
|
||||
|
||||
component StandardPreviewObject: QtObject {
|
||||
required property string hostname
|
||||
required property string title
|
||||
required property string description
|
||||
required property int linkType // 0 = link, 1 = image
|
||||
}
|
||||
|
||||
component ThumbnailObject: QtObject {
|
||||
required property int width
|
||||
required property int height
|
||||
required property string url
|
||||
required property string dataUri
|
||||
}
|
||||
|
||||
LinksMessageView {
|
||||
id: linksMessageView
|
||||
|
||||
anchors.fill: parent
|
||||
|
||||
store: {}
|
||||
messageStore: {}
|
||||
isOnline: true
|
||||
playAnimations: true
|
||||
linkPreviewModel: mockedLinkPreviewModel
|
||||
gifLinks: [ "https://media.tenor.com/qN_ytiwLh24AAAAC/cold.gif" ]
|
||||
isCurrentUser: true
|
||||
|
||||
gifUnfurlingEnabled: false
|
||||
canAskToUnfurlGifs: true
|
||||
onImageClicked: {
|
||||
console.log("image clicked")
|
||||
}
|
||||
|
@ -148,18 +57,28 @@ SplitView {
|
|||
}
|
||||
CheckBox {
|
||||
text: qsTr("Enabled")
|
||||
checked: RootStore.gifUnfurlingEnabled
|
||||
onToggled: RootStore.gifUnfurlingEnabled = !RootStore.gifUnfurlingEnabled
|
||||
checked: linksMessageView.gifUnfurlingEnabled
|
||||
onToggled: linksMessageView.gifUnfurlingEnabled = !linksMessageView.gifUnfurlingEnabled
|
||||
}
|
||||
CheckBox {
|
||||
text: qsTr("Never ask about GIF unfurling again")
|
||||
checked: RootStore.neverAskAboutUnfurlingAgain
|
||||
onClicked: RootStore.neverAskAboutUnfurlingAgain = !RootStore.neverAskAboutUnfurlingAgain
|
||||
text: qsTr("Can ask about GIF unfurling")
|
||||
checked: linksMessageView.canAskToUnfurlGifs
|
||||
onClicked: linksMessageView.canAskToUnfurlGifs = !linksMessageView.canAskToUnfurlGifs
|
||||
}
|
||||
Button {
|
||||
text: qsTr("Reset local `askAboutUnfurling` setting")
|
||||
onClicked: linksMessageView.resetLocalAskAboutUnfurling()
|
||||
}
|
||||
CheckBox {
|
||||
text: qsTr("Play animations")
|
||||
checked: linksMessageView.playAnimations
|
||||
onToggled: linksMessageView.playAnimations = !linksMessageView.playAnimations
|
||||
}
|
||||
CheckBox {
|
||||
text: qsTr("Is online")
|
||||
checked: linksMessageView.isOnline
|
||||
onToggled: linksMessageView.isOnline = !linksMessageView.isOnline
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ SplitView {
|
|||
id: fakeUsersModel
|
||||
}
|
||||
|
||||
ListModel {
|
||||
LinkPreviewModel {
|
||||
id: fakeLinksModel
|
||||
}
|
||||
|
||||
|
@ -172,19 +172,12 @@ SplitView {
|
|||
fakeLinksModel.clear()
|
||||
words.forEach(function(word){
|
||||
if(Utils.isURL(word)) {
|
||||
fakeLinksModel.append({
|
||||
url: encodeURI(word),
|
||||
unfurled: d.linkPreviewsEnabled,
|
||||
immutable: !d.linkPreviewsEnabled,
|
||||
hostname: Math.floor(Math.random() * 2) ? "youtube.com" : "",
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V",
|
||||
description: "This is the description of the link",
|
||||
linkType: Math.floor(Math.random() * 3),
|
||||
thumbnailWidth: 480,
|
||||
thumbnailHeight: 360,
|
||||
thumbnailUrl: "https://picsum.photos/480/360?random=1",
|
||||
thumbnailDataUri: ""
|
||||
})
|
||||
const linkPreview = fakeLinksModel.getStandardLinkPreview()
|
||||
linkPreview.url = encodeURI(word)
|
||||
linkPreview.unfurled = Math.random() > 0.2
|
||||
linkPreview.immutable = !d.linkPreviewsEnabled
|
||||
linkPreview.empty = Math.random() > 0.7
|
||||
fakeLinksModel.append(linkPreview)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,86 +0,0 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import shared.controls.chat 1.0
|
||||
import utils 1.0
|
||||
|
||||
SplitView {
|
||||
id: root
|
||||
|
||||
|
||||
|
||||
property bool globalUtilsReady: false
|
||||
|
||||
// globalUtilsInst mock
|
||||
QtObject {
|
||||
function getEmojiHashAsJson(publicKey) {
|
||||
return JSON.stringify(["👨🏻🍼", "🏃🏿♂️", "🌇", "🤶🏿", "🏮","🤷🏻♂️", "🤦🏻", "📣", "🤎", "👷🏽", "😺", "🥞", "🔃", "🧝🏽♂️"])
|
||||
}
|
||||
function getColorId(publicKey) { return 4 }
|
||||
|
||||
function getCompressedPk(publicKey) { return "zx3sh" + publicKey }
|
||||
|
||||
function getColorHashAsJson(publicKey) {
|
||||
return JSON.stringify([{4: 0, segmentLength: 1},
|
||||
{5: 19, segmentLength: 2}])
|
||||
}
|
||||
|
||||
function isCompressedPubKey(publicKey) { return true }
|
||||
|
||||
Component.onCompleted: {
|
||||
Utils.globalUtilsInst = this
|
||||
root.globalUtilsReady = true
|
||||
|
||||
}
|
||||
Component.onDestruction: {
|
||||
root.globalUtilsReady = false
|
||||
Utils.globalUtilsInst = {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Pane {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
active: root.globalUtilsReady
|
||||
sourceComponent: UserProfileCard {
|
||||
id: userProfileCard
|
||||
userName: nameInput.text
|
||||
userPublicKey: "0x1234567890"
|
||||
userBio: bioInput.text
|
||||
userImage: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAAlklEQVR4nOzW0QmDQBAG4SSkl7SUQlJGCrElq9F3QdjjVhh/5nv3cFhY9vUIYQiNITSG0BhCExPynn1gWf9bx498P7/
|
||||
nzPcxEzGExhBdJGYihtAYQlO+tUZvqrPbqeudo5iJGEJjCE15a3VtodH3q2ImYgiNITTlTdG1nUZ5a92VITQxITFiJmIIjSE0htAYQrMHAAD//+wwFVpz+yqXAAAAAElFTkSuQmCC"
|
||||
ensVerified: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Pane {
|
||||
SplitView.fillWidth: true
|
||||
SplitView.fillHeight: true
|
||||
SplitView.minimumWidth: 300
|
||||
|
||||
ColumnLayout {
|
||||
Label {
|
||||
text: "userName"
|
||||
}
|
||||
TextField {
|
||||
id: nameInput
|
||||
text: "John Doe"
|
||||
}
|
||||
Label {
|
||||
text: "userBio"
|
||||
}
|
||||
TextField {
|
||||
id: bioInput
|
||||
text: "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed non risus. Suspendisse lectus tortor, dignissim sit amet, adipiscing nec, ultricies sed, dolor."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// https://www.figma.com/file/Mr3rqxxgKJ2zMQ06UAKiWL/💬-Chat⎜Desktop?type=design&node-id=21961-655678&mode=design&t=JiMnPfMaLPWlrFK3-0
|
|
@ -0,0 +1,136 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
ListModel {
|
||||
id: root
|
||||
|
||||
|
||||
function getStandardLinkPreview() {
|
||||
const preview = Object.assign({}, emptyObject)
|
||||
preview.url = "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
preview.previewType = 1
|
||||
preview.standardPreview = {}
|
||||
preview.standardPreviewThumbnail = {}
|
||||
preview.standardPreview.hostname = "www.youtube.com"
|
||||
preview.standardPreview.title = "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
preview.standardPreview.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..."
|
||||
preview.standardPreview.linkType = 0
|
||||
preview.standardPreviewThumbnail.width = 480
|
||||
preview.standardPreviewThumbnail.height = 360
|
||||
preview.standardPreviewThumbnail.url = "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
preview.standardPreviewThumbnail.dataUri = ""
|
||||
return preview
|
||||
}
|
||||
|
||||
function getImageLinkPreview() {
|
||||
const preview = Object.assign({}, emptyObject)
|
||||
preview.url = "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
preview.previewType = 1
|
||||
preview.standardPreview = {}
|
||||
preview.standardPreviewThumbnail = {}
|
||||
preview.standardPreview.hostname = "i.ytimg.com"
|
||||
preview.standardPreview.title = "Image_link_preview.png"
|
||||
preview.standardPreview.description = "Image link preview"
|
||||
preview.standardPreview.linkType = 1
|
||||
preview.standardPreviewThumbnail.width = 480
|
||||
preview.standardPreviewThumbnail.height = 360
|
||||
preview.standardPreviewThumbnail.url = "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
preview.standardPreviewThumbnail.dataUri = ""
|
||||
return preview
|
||||
}
|
||||
|
||||
function getCommunityLinkPreview() {
|
||||
const preview = Object.assign({}, emptyObject)
|
||||
preview.url = "https://status.app/c/ixiACjAKDlRlc3QgQ29tbXVuaXR5Eg9PcGVuIGZvciBhbnlvbmUYdiIHI0ZGMDAwMCoCHwkD#zQ3shnd55dNx9yTihuL6XMbmyM6UNjzU6jk77h5Js31jxcT5V"
|
||||
preview.previewType = 3
|
||||
preview.statusCommunityPreview = {}
|
||||
preview.statusCommunityPreview.communityId = "zQ3shnd55dNx9yTihuL6XMbmyM6UNjzU6jk77h5Js31jxcT5V"
|
||||
preview.statusCommunityPreview.displayName = "Test community display name"
|
||||
preview.statusCommunityPreview.description = "Test community description"
|
||||
preview.statusCommunityPreview.membersCount = 10
|
||||
preview.statusCommunityPreview.color = "#123456"
|
||||
preview.statusCommunityPreviewIcon = {}
|
||||
preview.statusCommunityPreviewIcon.width = 64
|
||||
preview.statusCommunityPreviewIcon.height = 64
|
||||
preview.statusCommunityPreviewIcon.url = "https://picsum.photos/64/64?random=1"
|
||||
preview.statusCommunityPreviewIcon.dataUri = ""
|
||||
preview.statusCommunityPreviewBanner = {}
|
||||
preview.statusCommunityPreviewBanner.width = 320
|
||||
preview.statusCommunityPreviewBanner.height = 180
|
||||
preview.statusCommunityPreviewBanner.url = "https://picsum.photos/320/180?random=1"
|
||||
preview.statusCommunityPreviewBanner.dataUri = ""
|
||||
return preview
|
||||
}
|
||||
|
||||
function getChannelLinkPreview() {
|
||||
const preview = Object.assign({}, emptyObject)
|
||||
preview.url = "https://status.app/c/ixiACjAKDlRlc3QgQ29tbXVuaXR5Eg9PcGVuIGZvciBhbnlvbmUYdiIHI0ZGMDAwMCoCHwkD#zQ3shnd55dNx9yTihuL6XMbmyM6UNjzU6jk77h5Js31jxcT5V"
|
||||
preview.previewType = 4
|
||||
preview.statusCommunityChannelPreview = {}
|
||||
preview.statusCommunityChannelPreview.channelUuid = "zQ3shnd55dNx9yTihuL6XMbmyM6UNjzU6jk77h5Js31jxcT5V"
|
||||
preview.statusCommunityChannelPreview.emoji = "👋"
|
||||
preview.statusCommunityChannelPreview.displayName = "general"
|
||||
preview.statusCommunityChannelPreview.description = "Test channel description"
|
||||
preview.statusCommunityChannelPreview.color = "#122456"
|
||||
preview.statusCommunityChannelCommunityPreview = {}
|
||||
preview.statusCommunityChannelCommunityPreview.communityId = "zQ3shnd55dNx9yTihuL6XMbmyM6UNjzU6jk77h5Js31jxcT5V"
|
||||
preview.statusCommunityChannelCommunityPreview.displayName = "Doodles"
|
||||
preview.statusCommunityChannelCommunityPreview.description = "Test community description"
|
||||
preview.statusCommunityChannelCommunityPreview.membersCount = 10
|
||||
preview.statusCommunityChannelCommunityPreview.color = "#123456"
|
||||
preview.statusCommunityChannelCommunityPreviewIcon = {}
|
||||
preview.statusCommunityChannelCommunityPreviewIcon.width = 64
|
||||
preview.statusCommunityChannelCommunityPreviewIcon.height = 64
|
||||
preview.statusCommunityChannelCommunityPreviewIcon.url = "https://picsum.photos/64/64?random=1"
|
||||
preview.statusCommunityChannelCommunityPreviewIcon.dataUri = ""
|
||||
preview.statusCommunityChannelCommunityPreviewBanner = {}
|
||||
preview.statusCommunityChannelCommunityPreviewBanner.width = 320
|
||||
preview.statusCommunityChannelCommunityPreviewBanner.height = 180
|
||||
preview.statusCommunityChannelCommunityPreviewBanner.url = "https://picsum.photos/320/180?random=1"
|
||||
preview.statusCommunityChannelCommunityPreviewBanner.dataUri = ""
|
||||
return preview
|
||||
}
|
||||
|
||||
function getContactLinkPreview() {
|
||||
const preview = Object.assign({}, emptyObject)
|
||||
preview.url = "https://status.app/u/Ow==#zQ3shgmVJjmwwhkfAemjDizYJtv9nzot7QD4iRJ52ZkgdU6Ci"
|
||||
preview.previewType = 2
|
||||
preview.statusContactPreview = {}
|
||||
preview.statusContactPreview.publicKey = "zQ3shgmVJjmwwhkfAemjDizYJtv9nzot7QD4iRJ52ZkgdU6Ci"
|
||||
preview.statusContactPreview.displayName = "Test contact display name"
|
||||
preview.statusContactPreview.description = "Test description"
|
||||
preview.statusContactPreviewThumbnail = {}
|
||||
preview.statusContactPreviewThumbnail.width = 64
|
||||
preview.statusContactPreviewThumbnail.height = 64
|
||||
preview.statusContactPreviewThumbnail.url = "https://picsum.photos/64/64?random=1"
|
||||
preview.statusContactPreviewThumbnail.dataUri = ""
|
||||
return preview
|
||||
}
|
||||
|
||||
readonly property var emptyObject: {
|
||||
"unfurled": true,
|
||||
"immutable": false,
|
||||
"empty": false,
|
||||
"url": "",
|
||||
"previewType": 1,
|
||||
"standardPreview": {},
|
||||
"standardPreviewThumbnail": {},
|
||||
"statusContactPreview": {},
|
||||
"statusContactPreviewThumbnail": {},
|
||||
"statusCommunityPreview": {},
|
||||
"statusCommunityPreviewIcon": {},
|
||||
"statusCommunityPreviewBanner": {},
|
||||
"statusCommunityChannelPreview": {},
|
||||
"statusCommunityChannelCommunityPreview": {},
|
||||
"statusCommunityChannelCommunityPreviewIcon": {},
|
||||
"statusCommunityChannelCommunityPreviewBanner": {},
|
||||
}
|
||||
|
||||
// Create the model dynamically, because `ListElement` doesnt suppport nested elements
|
||||
Component.onCompleted: {
|
||||
append(getStandardLinkPreview())
|
||||
append(getImageLinkPreview())
|
||||
append(getCommunityLinkPreview())
|
||||
append(getChannelLinkPreview())
|
||||
append(getContactLinkPreview())
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@ ChannelsModel 1.0 ChannelsModel.qml
|
|||
CollectiblesModel 1.0 CollectiblesModel.qml
|
||||
FeesModel 1.0 FeesModel.qml
|
||||
IconModel 1.0 IconModel.qml
|
||||
LinkPreviewModel 1.0 LinkPreviewModel.qml
|
||||
MintedTokensModel 1.0 MintedTokensModel.qml
|
||||
RecipientModel 1.0 RecipientModel.qml
|
||||
TokenHoldersModel 1.0 TokenHoldersModel.qml
|
||||
|
|
|
@ -80,6 +80,30 @@ QtObject {
|
|||
return num.toLocaleString(locale, 'f', precision)
|
||||
}
|
||||
|
||||
function numberToLocaleStringInCompactForm(num, locale = null) {
|
||||
locale = locale || Qt.locale()
|
||||
const numberOfDigits = integralPartLength(num)
|
||||
let oneArgStrFormat = "%1"
|
||||
let formattedNumber = num
|
||||
let multiplier = 1
|
||||
if(numberOfDigits >=4 && numberOfDigits < 7) { // 1K - 999K
|
||||
multiplier = 1 / 1000
|
||||
oneArgStrFormat = qsTr("%1K", "Thousand")
|
||||
} else if(numberOfDigits >= 7 && numberOfDigits < 10) { // 1M - 999M
|
||||
multiplier = 1 / 1000000
|
||||
oneArgStrFormat = qsTr("%1M", "Million")
|
||||
} else if(numberOfDigits >= 10 && numberOfDigits < 13) { // 1B - 999B
|
||||
multiplier = 1 / 1000000000
|
||||
oneArgStrFormat = qsTr("%1B", "Billion")
|
||||
} else if(numberOfDigits >= 13 && numberOfDigits < 16) { // 1T - 999T
|
||||
multiplier = 1 / 1000000000000
|
||||
oneArgStrFormat = qsTr("%1T", "Trillion")
|
||||
}
|
||||
|
||||
const stringNumber = numberToLocaleString(num * multiplier, 2, locale)
|
||||
return oneArgStrFormat.arg(stripTrailingZeroes(stringNumber, locale))
|
||||
}
|
||||
|
||||
function numberFromLocaleString(num, locale = null) {
|
||||
locale = locale || Qt.locale()
|
||||
try {
|
||||
|
|
|
@ -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 |
|
@ -123,4 +123,168 @@ TestCase {
|
|||
compare(LocaleUtils.currencyAmountToLocaleString(
|
||||
data.amount, null, locale), data.amountString)
|
||||
}
|
||||
|
||||
function test_numberToLocaleStringInCompactForm_data() {
|
||||
return [
|
||||
{
|
||||
amount: NaN,
|
||||
amountString: "nan"
|
||||
},
|
||||
{
|
||||
amount: null,
|
||||
amountString: "0"
|
||||
},
|
||||
{
|
||||
amount: "",
|
||||
amountString: "0"
|
||||
},
|
||||
{
|
||||
amount: "string",
|
||||
amountString: "nan"
|
||||
},
|
||||
{
|
||||
amount: {},
|
||||
amountString: "nan"
|
||||
},
|
||||
{
|
||||
amount: -1,
|
||||
amountString: "-1"
|
||||
},
|
||||
{
|
||||
amount: -1.1,
|
||||
amountString: "-1.1"
|
||||
},
|
||||
{
|
||||
amount: -1.1234,
|
||||
amountString: "-1.12"
|
||||
},
|
||||
{
|
||||
amount: -1000,
|
||||
amountString: "-1K"
|
||||
},
|
||||
{
|
||||
amount: -1000.1,
|
||||
amountString: "-1K"
|
||||
},
|
||||
{
|
||||
amount: -100000,
|
||||
amountString: "-100K"
|
||||
},
|
||||
{
|
||||
amount: -1001,
|
||||
amountString: "-1K"
|
||||
},
|
||||
{
|
||||
amount: -1100,
|
||||
amountString: "-1.1K"
|
||||
},
|
||||
{
|
||||
amount: -1000000,
|
||||
amountString: "-1M"
|
||||
},
|
||||
{
|
||||
amount: -1100000.123,
|
||||
amountString: "-1.1M"
|
||||
},
|
||||
{
|
||||
amount: -1000000000,
|
||||
amountString: "-1B"
|
||||
},
|
||||
{
|
||||
amount: -1100000000,
|
||||
amountString: "-1.1B"
|
||||
},
|
||||
{
|
||||
amount: -1000000000.123,
|
||||
amountString: "-1B"
|
||||
},
|
||||
{
|
||||
amount: -1000000000000,
|
||||
amountString: "-1T"
|
||||
},
|
||||
{
|
||||
amount: -999000000000000,
|
||||
amountString: "-999T"
|
||||
},
|
||||
{
|
||||
amount: -1000000000000000,
|
||||
amountString: "-1,000,000,000,000,000"
|
||||
},
|
||||
{
|
||||
amount: 0,
|
||||
amountString: "0"
|
||||
},
|
||||
{
|
||||
amount: 1,
|
||||
amountString: "1"
|
||||
},
|
||||
{
|
||||
amount: 1.1,
|
||||
amountString: "1.1"
|
||||
},
|
||||
{
|
||||
amount: 1.1234,
|
||||
amountString: "1.12"
|
||||
},
|
||||
{
|
||||
amount: 1000,
|
||||
amountString: "1K"
|
||||
},
|
||||
{
|
||||
amount: 1000.1,
|
||||
amountString: "1K"
|
||||
},
|
||||
{
|
||||
amount: 100000,
|
||||
amountString: "100K"
|
||||
},
|
||||
{
|
||||
amount: 1001,
|
||||
amountString: "1K"
|
||||
},
|
||||
{
|
||||
amount: 1100,
|
||||
amountString: "1.1K"
|
||||
},
|
||||
{
|
||||
amount: 1000000,
|
||||
amountString: "1M"
|
||||
},
|
||||
{
|
||||
amount: 1100000.123,
|
||||
amountString: "1.1M"
|
||||
},
|
||||
{
|
||||
amount: 1000000000,
|
||||
amountString: "1B"
|
||||
},
|
||||
{
|
||||
amount: 1100000000,
|
||||
amountString: "1.1B"
|
||||
},
|
||||
{
|
||||
amount: 1000000000.123,
|
||||
amountString: "1B"
|
||||
},
|
||||
{
|
||||
amount: 1000000000000,
|
||||
amountString: "1T"
|
||||
},
|
||||
{
|
||||
amount: 999000000000000,
|
||||
amountString: "999T"
|
||||
},
|
||||
{
|
||||
amount: 1000000000000000,
|
||||
amountString: "1,000,000,000,000,000"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function test_numberToLocaleStringInCompactForm(data) {
|
||||
const locale = Qt.locale("en_US")
|
||||
|
||||
compare(LocaleUtils.numberToLocaleStringInCompactForm(
|
||||
data.amount, locale), data.amountString)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -277,6 +277,10 @@ Item {
|
|||
popups.openConfirmExternalLinkPopup(link, domain)
|
||||
}
|
||||
|
||||
function onActivateDeepLink(link: string) {
|
||||
appMain.rootStore.mainModuleInst.activateStatusDeepLink(link)
|
||||
}
|
||||
|
||||
function onPlaySendMessageSound() {
|
||||
sendMessageSound.stop()
|
||||
sendMessageSound.play()
|
||||
|
|
|
@ -6,7 +6,7 @@ import QtQuick.Layouts 1.15
|
|||
import StatusQ.Core 0.1
|
||||
|
||||
import shared.status 1.0
|
||||
import shared.controls.chat 1.0
|
||||
import shared.controls.delegates 1.0
|
||||
|
||||
import utils 1.0
|
||||
|
||||
|
@ -99,57 +99,8 @@ Control {
|
|||
Repeater {
|
||||
id: linkPreviewRepeater
|
||||
model: d.filteredModel
|
||||
delegate: LinkPreviewMiniCard {
|
||||
// Model properties
|
||||
|
||||
delegate: LinkPreviewMiniCardDelegate {
|
||||
required property int index
|
||||
required property bool unfurled
|
||||
required property bool empty
|
||||
required property string url
|
||||
required property bool immutable
|
||||
required property int previewType
|
||||
required property var standardPreview
|
||||
required property var standardPreviewThumbnail
|
||||
required property var statusContactPreview
|
||||
required property var statusContactPreviewThumbnail
|
||||
required property var statusCommunityPreview
|
||||
required property var statusCommunityPreviewIcon
|
||||
required property var statusCommunityPreviewBanner
|
||||
required property var statusCommunityChannelPreview
|
||||
required property var statusCommunityChannelCommunityPreview
|
||||
required property var statusCommunityChannelCommunityPreviewIcon
|
||||
required property var statusCommunityChannelCommunityPreviewBanner
|
||||
|
||||
readonly property var thumbnail: {
|
||||
switch (previewType) {
|
||||
case Constants.Standard:
|
||||
return standardPreviewThumbnail
|
||||
case Constants.StatusContact:
|
||||
return statusContactPreviewThumbnail
|
||||
case Constants.StatusCommunity:
|
||||
return statusCommunityPreviewIcon
|
||||
case Constants.StatusCommunityChannel:
|
||||
return statusCommunityChannelCommunityPreviewIcon
|
||||
}
|
||||
}
|
||||
|
||||
readonly property string thumbnailUrl: thumbnail ? thumbnail.url : ""
|
||||
readonly property string thumbnailDataUri: thumbnail ? thumbnail.dataUri : ""
|
||||
|
||||
|
||||
Layout.preferredHeight: 64
|
||||
|
||||
titleStr: standardPreview ? standardPreview.title : statusContactPreview ? statusContactPreview.displayName : ""
|
||||
domain: standardPreview ? standardPreview.hostname : "" //TODO: use domain when available
|
||||
favIconUrl: "" //TODO: use favicon when available
|
||||
communityName: statusCommunityPreview ? statusCommunityPreview.displayName : ""
|
||||
channelName: statusCommunityChannelPreview ? statusCommunityChannelPreview.displayName : ""
|
||||
|
||||
thumbnailImageUrl: thumbnailUrl.length > 0 ? thumbnailUrl : thumbnailDataUri
|
||||
type: getCardType(previewType, standardPreview)
|
||||
previewState: unfurled && !empty ? LinkPreviewMiniCard.State.Loaded :
|
||||
unfurled && empty ? LinkPreviewMiniCard.State.LoadingFailed :
|
||||
!unfurled ? LinkPreviewMiniCard.State.Loading : LinkPreviewMiniCard.State.Invalid
|
||||
|
||||
onClose: root.dismissLinkPreview(d.filteredModel.mapToSource(index))
|
||||
onRetry: root.linkReload(url)
|
||||
|
|
|
@ -6,27 +6,24 @@ 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
|
||||
|
||||
/// The title of the callout card
|
||||
required property string title
|
||||
required property string description
|
||||
required property string footer
|
||||
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
|
||||
|
||||
property StatusAssetSettings logoSettings: StatusAssetSettings {
|
||||
width: 28
|
||||
height: 28
|
||||
bgRadius: bgWidth / 2
|
||||
}
|
||||
|
||||
property string bannerImageSource: ""
|
||||
|
||||
signal clicked(var mouse)
|
||||
|
||||
borderWidth: 1
|
||||
|
@ -41,71 +38,103 @@ CalloutCard {
|
|||
}
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
StatusImage {
|
||||
id: bannerImage
|
||||
Loader {
|
||||
id: bannerImageLoader
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: d.bannerImageMargins
|
||||
Layout.rightMargin: d.bannerImageMargins
|
||||
Layout.topMargin: d.bannerImageMargins
|
||||
Layout.preferredHeight: 170
|
||||
active: !!d.bannerImageSource
|
||||
sourceComponent: StatusImage {
|
||||
id: bannerImage
|
||||
asynchronous: true
|
||||
source: root.bannerImageSource
|
||||
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: root.description.length ? 28 : 72
|
||||
Layout.maximumHeight: description.text.length ? 28 : 72
|
||||
Layout.minimumHeight: 18
|
||||
StatusSmartIdenticon {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignTop
|
||||
id: logo
|
||||
Layout.alignment: Qt.AlignTop
|
||||
Layout.preferredWidth: 28
|
||||
Layout.preferredHeight: 28
|
||||
asset: root.logoSettings
|
||||
name: root.logoSettings.name
|
||||
visible: !!root.logoSettings.name.length || !!root.logoSettings.emoji.length
|
||||
asset.width: width
|
||||
asset.height: height
|
||||
visible: false
|
||||
}
|
||||
StatusBaseText {
|
||||
text: root.title
|
||||
id: title
|
||||
// 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: Text.AlignVCenter
|
||||
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
|
||||
}
|
||||
StatusBaseText {
|
||||
id: description
|
||||
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
|
||||
visible: description.text.length
|
||||
}
|
||||
StatusBaseText {
|
||||
id: linkSite
|
||||
Loader {
|
||||
id: footerLoader
|
||||
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
|
||||
visible: active
|
||||
sourceComponent: FooterText {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: root
|
||||
hoverEnabled: true
|
||||
|
@ -114,9 +143,151 @@ CalloutCard {
|
|||
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.activeMembersCount === -1 //TODO: remove magic number once we have activeMembersCount
|
||||
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.activeMembersCount > -1
|
||||
}
|
||||
FooterText {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
color: Theme.palette.directColor1
|
||||
text: LocaleUtils.numberToLocaleStringInCompactForm(communityData.activeMembersCount)
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
visible: communityData.activeMembersCount > -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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: true; 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: ""
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import shared 1.0
|
|||
|
||||
import utils 1.0
|
||||
|
||||
import "./private" 1.0
|
||||
|
||||
CalloutCard {
|
||||
id: root
|
||||
|
||||
|
@ -20,45 +22,11 @@ CalloutCard {
|
|||
Loaded
|
||||
}
|
||||
|
||||
enum Type {
|
||||
Unknown = 0,
|
||||
Link,
|
||||
Image,
|
||||
Community,
|
||||
Channel,
|
||||
User
|
||||
}
|
||||
readonly property LinkData linkData: LinkData { }
|
||||
readonly property UserData userData: UserData { }
|
||||
readonly property CommunityData communityData: CommunityData { }
|
||||
readonly property ChannelData channelData: ChannelData { }
|
||||
|
||||
function getCardType(previewType, standardLinkPreview) {
|
||||
switch (previewType) {
|
||||
case Constants.StatusContact:
|
||||
return LinkPreviewMiniCard.Type.User
|
||||
case Constants.StatusCommunity:
|
||||
return LinkPreviewMiniCard.Type.Community
|
||||
case Constants.StatusCommunityChannel:
|
||||
return LinkPreviewMiniCard.Type.Channel
|
||||
case Constants.Standard:
|
||||
if (!standardLinkPreview)
|
||||
return LinkPreviewMiniCard.Type.Unknown
|
||||
switch (standardLinkPreview.linkType) {
|
||||
case Constants.StandardLinkPreviewType.Link:
|
||||
return LinkPreviewMiniCard.Type.Link
|
||||
case Constants.StandardLinkPreviewType.Image:
|
||||
return LinkPreviewMiniCard.Type.Image
|
||||
default:
|
||||
return LinkPreviewMiniCard.Type.Unknown
|
||||
}
|
||||
default:
|
||||
return LinkPreviewMiniCard.Type.Unknown
|
||||
}
|
||||
}
|
||||
|
||||
required property string titleStr
|
||||
required property string domain
|
||||
required property string communityName
|
||||
required property string channelName
|
||||
required property url favIconUrl
|
||||
required property url thumbnailImageUrl
|
||||
required property int previewState
|
||||
required property int type
|
||||
|
||||
|
@ -106,47 +74,78 @@ CalloutCard {
|
|||
},
|
||||
State {
|
||||
name: "loaded"
|
||||
when: root.previewState === LinkPreviewMiniCard.State.Loaded && root.type === LinkPreviewMiniCard.Type.Link
|
||||
when: root.previewState === LinkPreviewMiniCard.State.Loaded &&
|
||||
root.type === Constants.LinkPreviewType.Standard &&
|
||||
root.linkData.type === Constants.StandardLinkPreviewType.Link
|
||||
PropertyChanges {
|
||||
target: root; visible: true; dashedBorder: false; borderWidth: 0;
|
||||
backgroundColor: root.containsMouse ? Theme.palette.directColor8 : Theme.palette.indirectColor1;
|
||||
borderColor: backgroundColor;
|
||||
}
|
||||
PropertyChanges { target: loadingAnimation; visible: false; }
|
||||
PropertyChanges { target: titleText; text: root.titleStr; color: Theme.palette.directColor1 }
|
||||
PropertyChanges { target: subtitleText; visible: true; }
|
||||
PropertyChanges { target: titleText; text: root.linkData.title; color: Theme.palette.directColor1 }
|
||||
PropertyChanges { target: subtitleText; visible: true; text: root.linkData.domain; }
|
||||
PropertyChanges { target: reloadButton; visible: false; }
|
||||
PropertyChanges { target: favIcon; visible: true }
|
||||
},
|
||||
State {
|
||||
name: "loadedImage"
|
||||
when: root.previewState === LinkPreviewMiniCard.State.Loaded && root.type === LinkPreviewMiniCard.Type.Image
|
||||
extend: "loaded"
|
||||
PropertyChanges { target: thumbnailImage; visible: root.thumbnailImageUrl != "" }
|
||||
PropertyChanges { target: favIcon; visible: true; name: root.domain; asset.isLetterIdenticon: true; asset.color: Theme.palette.baseColor2; }
|
||||
},
|
||||
State {
|
||||
name: "loadedCommunity"
|
||||
when: root.previewState === LinkPreviewMiniCard.State.Loaded && root.type === LinkPreviewMiniCard.Type.Community
|
||||
extend: "loaded"
|
||||
PropertyChanges { target: titleText; text: root.communityName; }
|
||||
},
|
||||
State {
|
||||
name: "loadedChannel"
|
||||
when: root.previewState === LinkPreviewMiniCard.State.Loaded && root.type === LinkPreviewMiniCard.Type.Channel
|
||||
extend: "loaded"
|
||||
PropertyChanges { target: titleText; text: root.communityName; Layout.fillWidth: false; Layout.maximumWidth: Math.min(92, implicitWidth); }
|
||||
PropertyChanges { target: secondTitleText; text: root.channelName; visible: true; }
|
||||
},
|
||||
State {
|
||||
name: "loadedUser"
|
||||
when: root.previewState === LinkPreviewMiniCard.State.Loaded && root.type === LinkPreviewMiniCard.Type.User
|
||||
extend: "loaded"
|
||||
PropertyChanges {
|
||||
target: favIcon
|
||||
visible: true
|
||||
name: root.titleStr
|
||||
asset.isLetterIdenticon: true
|
||||
name: root.linkData.title
|
||||
asset.isLetterIdenticon: !root.linkData.image
|
||||
asset.color: Theme.palette.baseColor2
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "loadedImage"
|
||||
when: root.previewState === LinkPreviewMiniCard.State.Loaded &&
|
||||
root.type === Constants.LinkPreviewType.Standard &&
|
||||
root.linkData.type === Constants.StandardLinkPreviewType.Image
|
||||
extend: "loaded"
|
||||
PropertyChanges { target: thumbnailImage; visible: root.linkData.thumbnail != ""; image.source: root.linkData.thumbnail; }
|
||||
PropertyChanges { target: favIcon; visible: true; name: root.linkData.domain; asset.isLetterIdenticon: true; asset.color: Theme.palette.baseColor2; }
|
||||
PropertyChanges { target: subtitleText; visible: true; text: root.linkData.domain; }
|
||||
},
|
||||
State {
|
||||
name: "loadedCommunity"
|
||||
when: root.previewState === LinkPreviewMiniCard.State.Loaded && root.type === Constants.LinkPreviewType.StatusCommunity
|
||||
extend: "loaded"
|
||||
PropertyChanges { target: titleText; text: root.communityData.name; }
|
||||
PropertyChanges { target: subtitleText; visible: true; text: Constants.externalStatusLink; }
|
||||
PropertyChanges {
|
||||
target: favIcon
|
||||
visible: true
|
||||
name: root.communityData.name
|
||||
asset.isLetterIdenticon: root.communityData.image.length === 0
|
||||
asset.color: root.communityData.color
|
||||
asset.name: root.communityData.image
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "loadedChannel"
|
||||
when: root.previewState === LinkPreviewMiniCard.State.Loaded && root.type === Constants.LinkPreviewType.StatusCommunityChannel
|
||||
extend: "loadedCommunity"
|
||||
PropertyChanges { target: titleText; text: root.channelData.communityData.name; Layout.fillWidth: false; Layout.maximumWidth: Math.min(92, implicitWidth); }
|
||||
PropertyChanges { target: secondTitleText; text: "#" + root.channelData.name; visible: true; }
|
||||
PropertyChanges {
|
||||
target: favIcon
|
||||
visible: true
|
||||
name: root.channelData.communityData.name
|
||||
asset.isLetterIdenticon: root.channelData.communityData.image.length === 0
|
||||
asset.color: root.channelData.communityData.color
|
||||
asset.name: root.channelData.communityData.image
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "loadedUser"
|
||||
when: root.previewState === LinkPreviewMiniCard.State.Loaded && root.type === Constants.LinkPreviewType.StatusContact
|
||||
extend: "loaded"
|
||||
PropertyChanges { target: titleText; text: root.userData.name; Layout.fillWidth: false; Layout.maximumWidth: Math.min(92, implicitWidth); }
|
||||
PropertyChanges { target: subtitleText; visible: true; text: Constants.externalStatusLink; }
|
||||
PropertyChanges {
|
||||
target: favIcon
|
||||
visible: true
|
||||
name: root.userData.name
|
||||
asset.name: root.userData.image
|
||||
asset.isLetterIdenticon: root.userData.image.length === 0
|
||||
asset.charactersLen: 2
|
||||
asset.color: Theme.palette.miscColor9
|
||||
}
|
||||
|
@ -174,8 +173,6 @@ CalloutCard {
|
|||
Layout.preferredWidth: 16
|
||||
Layout.preferredHeight: 16
|
||||
visible: false
|
||||
name: root.titleStr
|
||||
asset.name: root.favIconUrl
|
||||
asset.letterSize: asset.charactersLen == 1 ? 10 : 7
|
||||
}
|
||||
ColumnLayout {
|
||||
|
@ -190,7 +187,6 @@ CalloutCard {
|
|||
spacing: 0
|
||||
StatusBaseText {
|
||||
id: titleText
|
||||
text: root.titleStr
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
font.pixelSize: Style.current.additionalTextSize
|
||||
|
@ -227,7 +223,6 @@ CalloutCard {
|
|||
Layout.fillHeight: true
|
||||
font.pixelSize: Style.current.tertiaryTextFontSize
|
||||
color: Theme.palette.baseColor1
|
||||
text: root.domain
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
@ -239,7 +234,6 @@ CalloutCard {
|
|||
implicitWidth: 34
|
||||
implicitHeight: 34
|
||||
radius: 4
|
||||
image.source: root.thumbnailImageUrl
|
||||
visible: false
|
||||
}
|
||||
StatusFlatButton {
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import shared.status 1.0
|
||||
import shared.controls 1.0
|
||||
import shared.controls.chat 1.0
|
||||
|
||||
import utils 1.0
|
||||
|
||||
|
||||
CalloutCard {
|
||||
id: root
|
||||
|
||||
required property string userName
|
||||
required property string userPublicKey
|
||||
required property string userBio
|
||||
required property var userImage
|
||||
required property bool ensVerified
|
||||
|
||||
signal clicked()
|
||||
|
||||
implicitWidth: 305
|
||||
implicitHeight: 187
|
||||
|
||||
padding: 12
|
||||
|
||||
contentItem: ColumnLayout {
|
||||
spacing: 0
|
||||
UserImage {
|
||||
Layout.alignment: Qt.AlignLeft | Qt.AlignVCenter
|
||||
name: root.userName
|
||||
pubkey: root.userPublicKey
|
||||
image: root.userImage
|
||||
interactive: false
|
||||
imageWidth: 58
|
||||
imageHeight: imageWidth
|
||||
ensVerified: root.ensVerified
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: contactName
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 12
|
||||
font.pixelSize: Style.current.additionalTextSize
|
||||
font.weight: Font.Medium
|
||||
elide: Text.ElideRight
|
||||
text: root.userName
|
||||
}
|
||||
|
||||
EmojiHash {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: 4
|
||||
publicKey: root.userPublicKey
|
||||
oneRow: true
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.topMargin: 15
|
||||
font.pixelSize: Style.current.tertiaryTextFontSize
|
||||
color: Theme.palette.baseColor1
|
||||
text: root.userBio
|
||||
wrapMode: Text.WordWrap
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: root
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.clicked()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
property string name
|
||||
property string description
|
||||
property string emoji
|
||||
property string color
|
||||
readonly property CommunityData communityData: CommunityData {}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
property string name
|
||||
property string description
|
||||
property string banner
|
||||
property string image
|
||||
property string color
|
||||
property int membersCount
|
||||
property int activeMembersCount: -1 // TODO: implement this and remove the magic number
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
property string title
|
||||
property string description
|
||||
property string domain
|
||||
property string thumbnail
|
||||
property string image
|
||||
property int type
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
QtObject {
|
||||
property string name
|
||||
property string publicKey
|
||||
property string bio
|
||||
property string image
|
||||
property bool ensVerified
|
||||
}
|
|
@ -19,6 +19,5 @@ SendTransactionButton 1.0 SendTransactionButton.qml
|
|||
SentMessage 1.0 SentMessage.qml
|
||||
StateBubble 1.0 StateBubble.qml
|
||||
UserImage 1.0 UserImage.qml
|
||||
UserProfileCard 1.0 UserProfileCard.qml
|
||||
UsernameLabel 1.0 UsernameLabel.qml
|
||||
VerificationLabel 1.0 VerificationLabel.qml
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import shared.controls.chat 1.0
|
||||
|
||||
LinkPreviewCard {
|
||||
id: root
|
||||
|
||||
/*
|
||||
* Model properties
|
||||
* The following properties are required to be set by the user of this component
|
||||
* unfurled: Whether the link has been unfurled or not
|
||||
* empty: Whether the link preview is empty or not
|
||||
* url: The url of the link
|
||||
* immutable: Whether the link preview can be updated
|
||||
* previewType: The type of the preview. See Constants.LinkPreviewType
|
||||
* standardPreview: The standard preview data (title, description, linkType, hostname)
|
||||
* standardPreviewThumbnail: The standard preview thumbnail data (url, dataUri)
|
||||
* statusContactPreview: The status contact preview data (displayName, publicKey, description, icon)
|
||||
* statusContactPreviewThumbnail: The status contact preview thumbnail data (url, dataUri)
|
||||
* statusCommunityPreview: The status community preview data (communityId, displayName, description, membersCount, color)
|
||||
* statusCommunityPreviewIcon: The status community preview icon data (url, dataUri)
|
||||
* statusCommunityPreviewBanner: The status community preview banner data (url, dataUri)
|
||||
* statusCommunityChannelPreview: The status community channel preview data (channelId, displayName, description, emoji, color)
|
||||
* statusCommunityChannelCommunityPreview: The status community channel community preview data (communityId, displayName, description, membersCount, color)
|
||||
* statusCommunityChannelCommunityPreviewIcon: The status community channel community preview icon data (url, dataUri)
|
||||
* statusCommunityChannelCommunityPreviewBanner: The status community channel community preview banner data (url, dataUri)
|
||||
*/
|
||||
required property bool unfurled
|
||||
required property bool empty
|
||||
required property string url
|
||||
required property bool immutable
|
||||
required property int previewType
|
||||
required property var standardPreview
|
||||
required property var standardPreviewThumbnail
|
||||
required property var statusContactPreview
|
||||
required property var statusContactPreviewThumbnail
|
||||
required property var statusCommunityPreview
|
||||
required property var statusCommunityPreviewIcon
|
||||
required property var statusCommunityPreviewBanner
|
||||
required property var statusCommunityChannelPreview
|
||||
required property var statusCommunityChannelCommunityPreview
|
||||
required property var statusCommunityChannelCommunityPreviewIcon
|
||||
required property var statusCommunityChannelCommunityPreviewBanner
|
||||
|
||||
//View properties
|
||||
property bool isCurrentUser: false
|
||||
|
||||
leftTail: !isCurrentUser
|
||||
type: root.previewType
|
||||
linkData {
|
||||
title: standardPreview ? standardPreview.title : ""
|
||||
description: standardPreview ? standardPreview.description : ""
|
||||
domain: standardPreview ? standardPreview.hostname : "" //TODO: Use domainName when available
|
||||
thumbnail: standardPreviewThumbnail ? (standardPreviewThumbnail.url || standardPreviewThumbnail.dataUri) || "" : ""
|
||||
image: "" //TODO: usefavicon when available
|
||||
}
|
||||
userData {
|
||||
name: statusContactPreview ? statusContactPreview.displayName : ""
|
||||
publicKey: statusContactPreview ? statusContactPreview.publicKey : ""
|
||||
bio: statusContactPreview ? statusContactPreview.description : ""
|
||||
image: statusContactPreviewThumbnail ? (statusContactPreviewThumbnail.url || statusContactPreviewThumbnail.dataUri) || "" : ""
|
||||
ensVerified: false // not supported yet
|
||||
}
|
||||
communityData {
|
||||
name: statusCommunityPreview ? statusCommunityPreview.displayName : ""
|
||||
description: statusCommunityPreview ? statusCommunityPreview.description : ""
|
||||
banner: statusCommunityPreviewBanner ? (statusCommunityPreviewBanner.url || statusCommunityPreviewBanner.dataUri) || "" : ""
|
||||
image: statusCommunityPreviewIcon ? (statusCommunityPreviewIcon.url || statusCommunityPreviewIcon.dataUri) || "" : ""
|
||||
membersCount: statusCommunityPreview ? statusCommunityPreview.membersCount : 0
|
||||
color: statusCommunityPreview ? statusCommunityPreview.color : ""
|
||||
}
|
||||
channelData {
|
||||
name: statusCommunityChannelPreview ? statusCommunityChannelPreview.displayName : ""
|
||||
description: statusCommunityChannelPreview ? statusCommunityChannelPreview.description : ""
|
||||
emoji: statusCommunityChannelPreview ? statusCommunityChannelPreview.emoji : ""
|
||||
color: statusCommunityChannelPreview ? statusCommunityChannelPreview.color : ""
|
||||
communityData {
|
||||
name: statusCommunityChannelCommunityPreview ? statusCommunityChannelCommunityPreview.displayName : ""
|
||||
description: statusCommunityChannelCommunityPreview ? statusCommunityChannelCommunityPreview.description : ""
|
||||
banner: statusCommunityChannelCommunityPreviewBanner ? (statusCommunityChannelCommunityPreviewBanner.url || statusCommunityChannelCommunityPreviewBanner.dataUri) || "" : ""
|
||||
image: statusCommunityChannelCommunityPreviewIcon ? (statusCommunityChannelCommunityPreviewIcon.url || statusCommunityChannelCommunityPreviewIcon.dataUri) || "" : ""
|
||||
membersCount: statusCommunityChannelCommunityPreview ? statusCommunityChannelCommunityPreview.membersCount : 0
|
||||
color: statusCommunityChannelCommunityPreview ? statusCommunityChannelCommunityPreview.color : ""
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
import utils 1.0
|
||||
|
||||
import shared.controls.chat 1.0
|
||||
import shared.status 1.0
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
|
||||
CalloutCard {
|
||||
id: root
|
||||
|
||||
required property string link
|
||||
required property bool playAnimation
|
||||
required property bool isOnline
|
||||
required property bool isCurrentUser
|
||||
|
||||
readonly property bool isPlaying: linkImage.playing
|
||||
readonly property alias imageAlias: linkImage.imageAlias
|
||||
|
||||
|
||||
signal clicked(var mouse)
|
||||
|
||||
implicitWidth: linkImage.width
|
||||
implicitHeight: linkImage.height
|
||||
leftTail: !isCurrentUser
|
||||
|
||||
StatusChatImageLoader {
|
||||
id: linkImage
|
||||
|
||||
property bool localAnimationEnabled: true
|
||||
|
||||
objectName: "LinksMessageView_unfurledImageComponent_linkImage"
|
||||
anchors.centerIn: parent
|
||||
source: root.link
|
||||
imageWidth: 300
|
||||
isCurrentUser: root.isCurrentUser
|
||||
playing: root.playAnimation && localAnimationEnabled
|
||||
isOnline: root.isOnline
|
||||
asynchronous: true
|
||||
isAnimated: true
|
||||
onClicked: {
|
||||
if (isAnimated && !playing)
|
||||
localAnimationEnabled = true
|
||||
else
|
||||
root.clicked(mouse)
|
||||
}
|
||||
imageAlias.cache: localAnimationEnabled // GIFs can only loop/play properly with cache enabled
|
||||
Loader {
|
||||
width: 45
|
||||
height: 38
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 12
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 12
|
||||
active: linkImage.isAnimated && !linkImage.playing
|
||||
sourceComponent: Item {
|
||||
anchors.fill: parent
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
radius: Style.current.radius
|
||||
opacity: .4
|
||||
}
|
||||
StatusBaseText {
|
||||
anchors.centerIn: parent
|
||||
text: "GIF"
|
||||
font.pixelSize: 13
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: animationPlayingTimer
|
||||
interval: 10000
|
||||
running: linkImage.isAnimated && linkImage.playing
|
||||
onTriggered: { linkImage.localAnimationEnabled = false }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
import QtQuick 2.15
|
||||
|
||||
import utils 1.0
|
||||
import shared.controls.chat 1.0
|
||||
|
||||
LinkPreviewMiniCard {
|
||||
id: root
|
||||
|
||||
/*
|
||||
* Model properties
|
||||
* The following properties are required to be set by the user of this component
|
||||
* unfurled: Whether the link has been unfurled or not
|
||||
* empty: Whether the link preview is empty or not
|
||||
* url: The url of the link
|
||||
* immutable: Whether the link preview can be updated
|
||||
* previewType: The type of the preview. See Constants.LinkPreviewType
|
||||
* standardPreview: The standard preview data (title, description, linkType, hostname)
|
||||
* standardPreviewThumbnail: The standard preview thumbnail data (url, dataUri)
|
||||
* statusContactPreview: The status contact preview data (displayName, publicKey, description, icon)
|
||||
* statusContactPreviewThumbnail: The status contact preview thumbnail data (url, dataUri)
|
||||
* statusCommunityPreview: The status community preview data (communityId, displayName, description, membersCount, color)
|
||||
* statusCommunityPreviewIcon: The status community preview icon data (url, dataUri)
|
||||
* statusCommunityPreviewBanner: The status community preview banner data (url, dataUri)
|
||||
* statusCommunityChannelPreview: The status community channel preview data (channelId, displayName, description, emoji, color)
|
||||
* statusCommunityChannelCommunityPreview: The status community channel community preview data (communityId, displayName, description, membersCount, color)
|
||||
* statusCommunityChannelCommunityPreviewIcon: The status community channel community preview icon data (url, dataUri)
|
||||
* statusCommunityChannelCommunityPreviewBanner: The status community channel community preview banner data (url, dataUri)
|
||||
*/
|
||||
required property bool unfurled
|
||||
required property bool empty
|
||||
required property string url
|
||||
required property bool immutable
|
||||
required property int previewType
|
||||
required property var standardPreview
|
||||
required property var standardPreviewThumbnail
|
||||
required property var statusContactPreview
|
||||
required property var statusContactPreviewThumbnail
|
||||
required property var statusCommunityPreview
|
||||
required property var statusCommunityPreviewIcon
|
||||
required property var statusCommunityPreviewBanner
|
||||
required property var statusCommunityChannelPreview
|
||||
required property var statusCommunityChannelCommunityPreview
|
||||
required property var statusCommunityChannelCommunityPreviewIcon
|
||||
required property var statusCommunityChannelCommunityPreviewBanner
|
||||
|
||||
previewState: !root.unfurled ? LinkPreviewMiniCard.State.Loading : root.unfurled && !root.empty ? LinkPreviewMiniCard.State.Loaded : LinkPreviewMiniCard.State.LoadingFailed
|
||||
type: root.previewType
|
||||
|
||||
linkData {
|
||||
title: standardPreview ? standardPreview.title : ""
|
||||
description: standardPreview ? standardPreview.description : ""
|
||||
domain: standardPreview ? standardPreview.hostname : "" //TODO: Use domainName when available
|
||||
thumbnail: standardPreviewThumbnail ? (standardPreviewThumbnail.url || standardPreviewThumbnail.dataUri) || "" : ""
|
||||
image: "" //TODO: usefavicon when available
|
||||
type: standardPreview ? standardPreview.linkType : Constants.StandardLinkPreviewType.Link
|
||||
}
|
||||
userData {
|
||||
name: statusContactPreview ? statusContactPreview.displayName : ""
|
||||
publicKey: statusContactPreview ? statusContactPreview.publicKey : ""
|
||||
bio: statusContactPreview ? statusContactPreview.description : ""
|
||||
image: statusContactPreviewThumbnail ? (statusContactPreviewThumbnail.url || statusContactPreviewThumbnail.dataUri) || "" : ""
|
||||
ensVerified: false // not supported yet
|
||||
}
|
||||
communityData {
|
||||
name: statusCommunityPreview ? statusCommunityPreview.displayName : ""
|
||||
description: statusCommunityPreview ? statusCommunityPreview.description : ""
|
||||
banner: statusCommunityPreviewBanner ? (statusCommunityPreviewBanner.url || statusCommunityPreviewBanner.dataUri) || "" : ""
|
||||
image: statusCommunityPreviewIcon ? (statusCommunityPreviewIcon.url || statusCommunityPreviewIcon.dataUri) || "" : ""
|
||||
membersCount: statusCommunityPreview ? statusCommunityPreview.membersCount : 0
|
||||
color: statusCommunityPreview ? statusCommunityPreview.color : ""
|
||||
}
|
||||
channelData {
|
||||
name: statusCommunityChannelPreview ? statusCommunityChannelPreview.displayName : ""
|
||||
description: statusCommunityChannelPreview ? statusCommunityChannelPreview.description : ""
|
||||
emoji: statusCommunityChannelPreview ? statusCommunityChannelPreview.emoji : ""
|
||||
color: statusCommunityChannelPreview ? statusCommunityChannelPreview.color : ""
|
||||
communityData {
|
||||
name: statusCommunityChannelCommunityPreview ? statusCommunityChannelCommunityPreview.displayName : ""
|
||||
description: statusCommunityChannelCommunityPreview ? statusCommunityChannelCommunityPreview.description : ""
|
||||
banner: statusCommunityChannelCommunityPreviewBanner ? (statusCommunityChannelCommunityPreviewBanner.url || statusCommunityChannelCommunityPreviewBanner.dataUri) || "" : ""
|
||||
image: statusCommunityChannelCommunityPreviewIcon ? (statusCommunityChannelCommunityPreviewIcon.url || statusCommunityChannelCommunityPreviewIcon.dataUri) || "" : ""
|
||||
membersCount: statusCommunityChannelCommunityPreview ? statusCommunityChannelCommunityPreview.membersCount : 0
|
||||
color: statusCommunityChannelCommunityPreview ? statusCommunityChannelCommunityPreview.color : ""
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +1,4 @@
|
|||
ContactListItemDelegate 1.0 ContactListItemDelegate.qml
|
||||
LinkPreviewCardDelegate 1.0 LinkPreviewCardDelegate.qml
|
||||
LinkPreviewGifDelegate 1.0 LinkPreviewGifDelegate.qml
|
||||
LinkPreviewMiniCardDelegate 1.0 LinkPreviewMiniCardDelegate.qml
|
||||
|
|
|
@ -1201,7 +1201,7 @@ Rectangle {
|
|||
}
|
||||
control.fileUrlsAndSources = urls
|
||||
}
|
||||
onImageClicked: (chatImage) => Global.openImagePopup(chatImage)
|
||||
onImageClicked: (chatImage) => Global.openImagePopup(chatImage, "")
|
||||
onLinkReload: (link) => control.linkPreviewReloaded(link)
|
||||
onLinkClicked: (link) => Global.openLink(link)
|
||||
onEnableLinkPreview: () => control.enableLinkPreview()
|
||||
|
|
|
@ -7,6 +7,8 @@ StatusMenu {
|
|||
|
||||
property string url
|
||||
property string imageSource
|
||||
property string domain
|
||||
property bool requireConfirmationOnOpen: false
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
@ -43,6 +45,6 @@ StatusMenu {
|
|||
text: qsTr("Open link")
|
||||
icon.name: "browser"
|
||||
enabled: d.isUnfurled
|
||||
onTriggered: Global.openLink(root.url)
|
||||
onTriggered: requireConfirmationOnOpen ? Global.openLinkWithConfirmation(root.url, root.domain) : Global.openLink(root.url)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,24 +10,29 @@ import StatusQ.Components 0.1
|
|||
import shared.controls 1.0
|
||||
import shared.status 1.0
|
||||
import shared.panels 1.0
|
||||
import shared.stores 1.0
|
||||
import shared.controls.chat 1.0
|
||||
|
||||
import shared.controls.delegates 1.0
|
||||
|
||||
Flow {
|
||||
id: root
|
||||
|
||||
required property var store
|
||||
required property var messageStore
|
||||
required property bool isOnline
|
||||
required property bool playAnimations
|
||||
|
||||
required property var linkPreviewModel
|
||||
required property var gifLinks
|
||||
|
||||
required property bool isCurrentUser
|
||||
|
||||
required property bool gifUnfurlingEnabled
|
||||
required property bool canAskToUnfurlGifs
|
||||
|
||||
readonly property alias hoveredLink: linksRepeater.hoveredUrl
|
||||
property string highlightLink: ""
|
||||
|
||||
signal imageClicked(var image, var mouse, var imageSource, string url)
|
||||
signal imageClicked(var image, var mouse, string imageSource, string url)
|
||||
signal openContextMenu(var item, string url, string domain)
|
||||
signal setNeverAskAboutUnfurlingAgain(bool neverAskAgain)
|
||||
|
||||
function resetLocalAskAboutUnfurling() {
|
||||
d.localAskAboutUnfurling = true
|
||||
|
@ -45,15 +50,25 @@ Flow {
|
|||
Loader {
|
||||
visible: active
|
||||
active: root.gifLinks && root.gifLinks.length > 0
|
||||
&& !RootStore.gifUnfurlingEnabled
|
||||
&& d.localAskAboutUnfurling && !RootStore.neverAskAboutUnfurlingAgain
|
||||
&& !root.gifUnfurlingEnabled
|
||||
&& d.localAskAboutUnfurling && root.canAskToUnfurlGifs
|
||||
sourceComponent: enableLinkComponent
|
||||
}
|
||||
|
||||
Repeater {
|
||||
id: tempRepeater
|
||||
model: RootStore.gifUnfurlingEnabled ? gifLinks : []
|
||||
delegate: gifComponent
|
||||
visible: root.canAskToUnfurlGifs
|
||||
model: root.gifUnfurlingEnabled ? gifLinks : []
|
||||
|
||||
delegate: LinkPreviewGifDelegate {
|
||||
required property string modelData
|
||||
|
||||
link: modelData
|
||||
isOnline: root.isOnline
|
||||
isCurrentUser: root.isCurrentUser
|
||||
playAnimation: root.playAnimations
|
||||
onClicked: root.imageClicked(imageAlias, mouse, link, link)
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
|
@ -62,154 +77,26 @@ Flow {
|
|||
property string hoveredUrl: ""
|
||||
|
||||
model: root.linkPreviewModel
|
||||
delegate: Loader {
|
||||
id: linkMessageLoader
|
||||
// properties from the model
|
||||
|
||||
required property bool unfurled
|
||||
required property bool empty
|
||||
required property string url
|
||||
required property bool immutable
|
||||
required property int previewType
|
||||
required property var standardPreview
|
||||
required property var standardPreviewThumbnail
|
||||
required property var statusContactPreview
|
||||
required property var statusContactPreviewThumbnail
|
||||
required property var statusCommunityPreview
|
||||
required property var statusCommunityPreviewIcon
|
||||
required property var statusCommunityPreviewBanner
|
||||
required property var statusCommunityChannelPreview
|
||||
required property var statusCommunityChannelCommunityPreview
|
||||
required property var statusCommunityChannelCommunityPreviewIcon
|
||||
required property var statusCommunityChannelCommunityPreviewBanner
|
||||
|
||||
readonly property string hostname: standardPreview ? standardPreview.hostname : ""
|
||||
readonly property string title: standardPreview ? standardPreview.title : ""
|
||||
readonly property string description: standardPreview ? standardPreview.description : ""
|
||||
readonly property int standardLinkType: standardPreview ? standardPreview.linkType : ""
|
||||
readonly property int thumbnailWidth: standardPreviewThumbnail ? standardPreviewThumbnail.width : ""
|
||||
readonly property int thumbnailHeight: standardPreviewThumbnail ? standardPreviewThumbnail.height : ""
|
||||
readonly property string thumbnailUrl: standardPreviewThumbnail ? standardPreviewThumbnail.url : ""
|
||||
readonly property string thumbnailDataUri: standardPreviewThumbnail ? standardPreviewThumbnail.dataUri : ""
|
||||
|
||||
asynchronous: true
|
||||
active: unfurled && !empty
|
||||
|
||||
StateGroup {
|
||||
//Using StateGroup as a warkardound for https://bugreports.qt.io/browse/QTBUG-47796
|
||||
states: [
|
||||
State {
|
||||
name: "standardLinkPreview"
|
||||
when: linkMessageLoader.previewType === Constants.LinkPreviewType.Standard
|
||||
PropertyChanges { target: linkMessageLoader; sourceComponent: standardLinkPreviewCard }
|
||||
},
|
||||
State {
|
||||
name: "statusContactLinkPreview"
|
||||
when: linkMessageLoader.previewType === Constants.LinkPreviewType.StatusContact
|
||||
PropertyChanges { target: linkMessageLoader; sourceComponent: unfurledProfileLinkComponent }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: standardLinkPreviewCard
|
||||
LinkPreviewCard {
|
||||
leftTail: !root.isCurrentUser // WARNING: Is this by design?
|
||||
bannerImageSource: standardPreviewThumbnail ? standardPreviewThumbnail.url : ""
|
||||
title: standardPreview ? standardPreview.title : ""
|
||||
description: standardPreview ? standardPreview.description : ""
|
||||
footer: standardPreview ? standardPreview.hostname : ""
|
||||
highlight: root.highlightLink === url
|
||||
onClicked: (mouse) => {
|
||||
switch (mouse.button) {
|
||||
case Qt.RightButton:
|
||||
root.imageClicked(unfurledLink, mouse, "", url) // request a dumb context menu with just "copy/open link" items
|
||||
break
|
||||
default:
|
||||
Global.openLinkWithConfirmation(url, hostname)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: unfurledProfileLinkComponent
|
||||
UserProfileCard {
|
||||
id: unfurledProfileLink
|
||||
|
||||
leftTail: !root.isCurrentUser
|
||||
userName: statusContactPreview && statusContactPreview.displayName ? statusContactPreview.displayName : ""
|
||||
userPublicKey: statusContactPreview && statusContactPreview.publicKey ? statusContactPreview.publicKey : ""
|
||||
userBio: statusContactPreview && statusContactPreview.description ? statusContactPreview.description : ""
|
||||
userImage: statusContactPreviewThumbnail ? statusContactPreviewThumbnail.url : ""
|
||||
ensVerified: false // not supported yet
|
||||
onClicked: {
|
||||
Global.openProfilePopup(userPublicKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Remove this once we have gif support in new unfurling flow
|
||||
Component {
|
||||
id: gifComponent
|
||||
CalloutCard {
|
||||
implicitWidth: linkImage.width
|
||||
implicitHeight: linkImage.height
|
||||
leftTail: !root.isCurrentUser
|
||||
StatusChatImageLoader {
|
||||
id: linkImage
|
||||
readonly property bool globalAnimationEnabled: root.messageStore.playAnimation
|
||||
readonly property string urlLink: modelData
|
||||
property bool localAnimationEnabled: true
|
||||
objectName: "LinksMessageView_unfurledImageComponent_linkImage"
|
||||
anchors.centerIn: parent
|
||||
source: urlLink
|
||||
imageWidth: 300
|
||||
delegate: LinkPreviewCardDelegate {
|
||||
id: delegate
|
||||
isCurrentUser: root.isCurrentUser
|
||||
playing: globalAnimationEnabled && localAnimationEnabled
|
||||
isOnline: root.store.mainModuleInst.isOnline
|
||||
asynchronous: true
|
||||
isAnimated: true
|
||||
onClicked: {
|
||||
if (!playing)
|
||||
localAnimationEnabled = true
|
||||
else
|
||||
root.imageClicked(linkImage.imageAlias, mouse, source, urlLink)
|
||||
highlight: url === root.highlightLink
|
||||
onHoveredChanged: {
|
||||
linksRepeater.hoveredUrl = hovered ? url : ""
|
||||
}
|
||||
imageAlias.cache: localAnimationEnabled // GIFs can only loop/play properly with cache enabled
|
||||
Loader {
|
||||
width: 45
|
||||
height: 38
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: 12
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 12
|
||||
active: linkImage.isAnimated && !linkImage.playing
|
||||
sourceComponent: Item {
|
||||
anchors.fill: parent
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "black"
|
||||
radius: Style.current.radius
|
||||
opacity: .4
|
||||
onClicked: (mouse) => {
|
||||
if(mouse.button === Qt.RightButton) {
|
||||
const domain = previewType === Constants.LinkPreviewType.Standard ? linkData.domain : Constants.externalStatusLink
|
||||
root.openContextMenu(delegate, url, domain)
|
||||
return
|
||||
}
|
||||
StatusBaseText {
|
||||
anchors.centerIn: parent
|
||||
text: "GIF"
|
||||
font.pixelSize: 13
|
||||
color: "white"
|
||||
}
|
||||
}
|
||||
}
|
||||
Timer {
|
||||
id: animationPlayingTimer
|
||||
interval: 10000
|
||||
running: linkImage.isAnimated && linkImage.playing
|
||||
onTriggered: { linkImage.localAnimationEnabled = false }
|
||||
|
||||
if(previewType === Constants.LinkPreviewType.Standard) {
|
||||
Global.openLinkWithConfirmation(url, linkData.domain)
|
||||
return
|
||||
}
|
||||
|
||||
Global.activateDeepLink(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -308,7 +195,7 @@ Flow {
|
|||
text: qsTr("Don't ask me again")
|
||||
}
|
||||
}
|
||||
onClicked: RootStore.setNeverAskAboutUnfurlingAgain(true)
|
||||
onClicked: root.setNeverAskAboutUnfurlingAgain(true)
|
||||
Component.onCompleted: {
|
||||
background.radius = Style.current.padding;
|
||||
}
|
||||
|
@ -316,5 +203,5 @@ Flow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ import shared.controls 1.0
|
|||
import shared.popups 1.0
|
||||
import shared.views.chat 1.0
|
||||
import shared.controls.chat 1.0
|
||||
import shared.stores 1.0
|
||||
|
||||
import StatusQ.Core 0.1
|
||||
import StatusQ.Core.Theme 0.1
|
||||
|
@ -570,7 +571,7 @@ Loader {
|
|||
rootStore.chatCommunitySectionModule.switchToChannel(link.replace("#", ""))
|
||||
return
|
||||
} else if (Utils.isStatusDeepLink(link)) {
|
||||
rootStore.activateStatusDeepLink(link)
|
||||
Global.activateDeepLink(link)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -755,14 +756,20 @@ Loader {
|
|||
id: linksMessageView
|
||||
linkPreviewModel: root.linkPreviewModel
|
||||
gifLinks: root.gifLinks
|
||||
messageStore: root.messageStore
|
||||
store: root.rootStore
|
||||
playAnimations: root.messageStore.playAnimation
|
||||
isOnline: root.rootStore.mainModuleInst.isOnline
|
||||
isCurrentUser: root.amISender
|
||||
highlightLink: delegate.hoveredLink
|
||||
onImageClicked: (image, mouse, imageSource, url) => {
|
||||
d.onImageClicked(image, mouse, imageSource, url)
|
||||
}
|
||||
onOpenContextMenu: (item, url, domain) => {
|
||||
Global.openMenu(imageContextMenuComponent, item, { url: url, domain: domain, requireConfirmationOnOpen: true })
|
||||
}
|
||||
onHoveredLinkChanged: delegate.highlightedLink = linksMessageView.hoveredLink
|
||||
gifUnfurlingEnabled: RootStore.gifUnfurlingEnabled
|
||||
canAskToUnfurlGifs: !RootStore.neverAskAboutUnfurlingAgain
|
||||
onSetNeverAskAboutUnfurlingAgain: RootStore.setNeverAskAboutUnfurlingAgain(neverAskAgain)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -56,6 +56,7 @@ QtObject {
|
|||
|
||||
signal openLink(string link)
|
||||
signal openLinkWithConfirmation(string link, string domain)
|
||||
signal activateDeepLink(string link)
|
||||
|
||||
signal setNthEnabledSectionActive(int nthSection)
|
||||
signal appSectionBySectionTypeChanged(int sectionType, int subsection)
|
||||
|
|
Loading…
Reference in New Issue