feature: support unfurled Status links (contact/community/channel) (#12303)

* chore: move `LinkPreviewThumbnail` to a separate file
This commit is contained in:
Igor Sirotin 2023-10-13 14:36:07 +01:00 committed by GitHub
parent 9581e6deb6
commit 520d34240a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 1177 additions and 266 deletions

View File

@ -11,6 +11,9 @@ include ../../app_service/service/accounts/utils
QtObject: QtObject:
type Utils* = ref object of QObject type Utils* = ref object of QObject
proc isCompressedPubKey*(self: Utils, publicKey: string): bool
proc getDecompressedPk*(self: Utils, compressedKey: string): string
proc setup(self: Utils) = proc setup(self: Utils) =
self.QObject.setup self.QObject.setup
@ -129,13 +132,22 @@ QtObject:
result = escape_html(text) result = escape_html(text)
proc getEmojiHashAsJson*(self: Utils, publicKey: string): string {.slot.} = proc getEmojiHashAsJson*(self: Utils, publicKey: string): string {.slot.} =
procs_from_visual_identity_service.getEmojiHashAsJson(publicKey) var pk = publicKey
if self.isCompressedPubKey(publicKey):
pk = self.getDecompressedPk(publicKey)
procs_from_visual_identity_service.getEmojiHashAsJson(pk)
proc getColorHashAsJson*(self: Utils, publicKey: string): string {.slot.} = proc getColorHashAsJson*(self: Utils, publicKey: string): string {.slot.} =
procs_from_visual_identity_service.getColorHashAsJson(publicKey) var pk = publicKey
if self.isCompressedPubKey(publicKey):
pk = self.getDecompressedPk(publicKey)
procs_from_visual_identity_service.getColorHashAsJson(pk)
proc getColorId*(self: Utils, publicKey: string): int {.slot.} = proc getColorId*(self: Utils, publicKey: string): int {.slot.} =
int(procs_from_visual_identity_service.colorIdOf(publicKey)) var pk = publicKey
if self.isCompressedPubKey(publicKey):
pk = self.getDecompressedPk(publicKey)
int(procs_from_visual_identity_service.colorIdOf(pk))
proc getCompressedPk*(self: Utils, publicKey: string): string {.slot.} = proc getCompressedPk*(self: Utils, publicKey: string): string {.slot.} =
compressPk(publicKey) compressPk(publicKey)

View File

@ -192,8 +192,8 @@ proc getLinkPreviewEnabled*(self: Controller): bool =
proc canAskToEnableLinkPreview(self: Controller): bool = proc canAskToEnableLinkPreview(self: Controller): bool =
return self.linkPreviewPersistentSetting == LinkPreviewSetting.AlwaysAsk and self.linkPreviewCurrentMessageSetting == LinkPreviewSetting.AlwaysAsk return self.linkPreviewPersistentSetting == LinkPreviewSetting.AlwaysAsk and self.linkPreviewCurrentMessageSetting == LinkPreviewSetting.AlwaysAsk
proc setText*(self: Controller, text: string) = proc setText*(self: Controller, text: string, unfurlNewUrls: bool) =
if(text == ""): if text == "":
self.resetLinkPreviews() self.resetLinkPreviews()
return return
@ -204,7 +204,10 @@ proc setText*(self: Controller, text: string) =
let askToEnableLinkPreview = len(newUrls) > 0 and self.canAskToEnableLinkPreview() let askToEnableLinkPreview = len(newUrls) > 0 and self.canAskToEnableLinkPreview()
self.delegate.setAskToEnableLinkPreview(askToEnableLinkPreview) self.delegate.setAskToEnableLinkPreview(askToEnableLinkPreview)
if self.getLinkPreviewEnabled() and len(urls) > 0: if not unfurlNewUrls:
return
if self.getLinkPreviewEnabled() and len(newUrls) > 0:
self.messageService.asyncUnfurlUrls(newUrls) self.messageService.asyncUnfurlUrls(newUrls)
proc linkPreviewsFromCache*(self: Controller, urls: seq[string]): Table[string, LinkPreview] = proc linkPreviewsFromCache*(self: Controller, urls: seq[string]): Table[string, LinkPreview] =
@ -232,4 +235,4 @@ proc setLinkPreviewEnabled*(self: Controller, enabled: bool) =
self.linkPreviewPersistentSetting = LinkPreviewSetting.Disabled self.linkPreviewPersistentSetting = LinkPreviewSetting.Disabled
self.linkPreviewCurrentMessageSetting = LinkPreviewSetting.Disabled self.linkPreviewCurrentMessageSetting = LinkPreviewSetting.Disabled
self.delegate.setAskToEnableLinkPreview(false) self.delegate.setAskToEnableLinkPreview(false)

View File

@ -96,7 +96,7 @@ method isFavorite*(self: AccessInterface, item: GifDto): bool {.base.} =
method viewDidLoad*(self: AccessInterface) {.base.} = method viewDidLoad*(self: AccessInterface) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method setText*(self: AccessInterface, text: string) {.base.} = method setText*(self: AccessInterface, text: string, unfurlUrls: bool) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method setUrls*(self: AccessInterface, urls: seq[string]) {.base.} = method setUrls*(self: AccessInterface, urls: seq[string]) {.base.} =

View File

@ -152,8 +152,8 @@ method addToRecentsGif*(self: Module, item: GifDto) =
method isFavorite*(self: Module, item: GifDto): bool = method isFavorite*(self: Module, item: GifDto): bool =
return self.controller.isFavorite(item) return self.controller.isFavorite(item)
method setText*(self: Module, text: string) = method setText*(self: Module, text: string, unfurlUrls: bool) =
self.controller.setText(text) self.controller.setText(text, unfurlUrls)
method clearLinkPreviewCache*(self: Module) {.slot.} = method clearLinkPreviewCache*(self: Module) {.slot.} =
self.controller.clearLinkPreviewCache() self.controller.clearLinkPreviewCache()

View File

@ -51,9 +51,11 @@ QtObject:
msg: string, msg: string,
replyTo: string, replyTo: string,
contentType: int) {.slot.} = contentType: int) {.slot.} =
self.delegate.setText(msg, false)
self.delegate.sendChatMessage(msg, replyTo, contentType, self.linkPreviewModel.getUnfuledLinkPreviews()) self.delegate.sendChatMessage(msg, replyTo, contentType, self.linkPreviewModel.getUnfuledLinkPreviews())
proc sendImages*(self: View, imagePathsAndDataJson: string, msg: string, replyTo: string): string {.slot.} = proc sendImages*(self: View, imagePathsAndDataJson: string, msg: string, replyTo: string): string {.slot.} =
self.delegate.setText(msg, false)
self.delegate.sendImages(imagePathsAndDataJson, msg, replyTo, self.linkPreviewModel.getUnfuledLinkPreviews()) self.delegate.sendImages(imagePathsAndDataJson, msg, replyTo, self.linkPreviewModel.getUnfuledLinkPreviews())
proc acceptAddressRequest*(self: View, messageId: string , address: string) {.slot.} = proc acceptAddressRequest*(self: View, messageId: string , address: string) {.slot.} =
@ -212,7 +214,7 @@ QtObject:
# Currently used to fetch link previews, but could be used elsewhere # Currently used to fetch link previews, but could be used elsewhere
proc setText*(self: View, text: string) {.slot.} = proc setText*(self: View, text: string) {.slot.} =
self.delegate.setText(text) self.delegate.setText(text, true)
proc updateLinkPreviewsFromCache*(self: View, urls: seq[string]) = proc updateLinkPreviewsFromCache*(self: View, urls: seq[string]) =
let linkPreviews = self.delegate.linkPreviewsFromCache(urls) let linkPreviews = self.delegate.linkPreviewsFromCache(urls)
@ -252,4 +254,4 @@ QtObject:
self.loadLinkPreviews(links) self.loadLinkPreviews(links)
proc removeLinkPreviewData*(self: View, index: int) {.slot.} = proc removeLinkPreviewData*(self: View, index: int) {.slot.} =
self.linkPreviewModel.removePreviewData(index) self.linkPreviewModel.removePreviewData(index)

View File

@ -1,5 +1,7 @@
import strformat import strformat
import ../../../app_service/service/message/dto/link_preview import ../../../app_service/service/message/dto/link_preview
import ../../../app_service/service/message/dto/status_community_link_preview
import ../../../app_service/service/message/dto/status_community_channel_link_preview
type type
Item* = ref object Item* = ref object

View File

@ -1,20 +1,37 @@
import NimQml, strformat, tables, sequtils import NimQml, strformat, tables, sequtils
import ./link_preview_item import ./link_preview_item
import ../../../app_service/service/message/dto/link_preview import ../../../app_service/service/message/dto/link_preview
import ../../../app_service/service/message/dto/standard_link_preview
import ../../../app_service/service/message/dto/status_link_preview
import ../../../app_service/service/message/dto/status_contact_link_preview
import ../../../app_service/service/message/dto/status_community_link_preview
import ../../../app_service/service/message/dto/status_community_channel_link_preview
type type
ModelRole {.pure.} = enum ModelRole {.pure.} = enum
Url = UserRole + 1 Url = UserRole + 1
Unfurled Unfurled
Immutable Immutable
Hostname Empty
Title PreviewType
Description # Standard unfurled link (oembed, opengraph, image)
LinkType StandardPreview
ThumbnailWidth StandardPreviewThumbnail
ThumbnailHeight # Status contact
ThumbnailUrl StatusContactPreview
ThumbnailDataUri StatusContactPreviewThumbnail
# Status community
StatusCommunityPreview
StatusCommunityPreviewIcon
StatusCommunityPreviewBanner
# Status channel
StatusCommunityChannelPreview
# NOTE: I know "CommunityChannelCommunity" doesn't sound good,
# and we could use existing `StatusCommunityPreview` role for this,
# but I decided no to mess things around. So there we have it:
StatusCommunityChannelCommunityPreview
StatusCommunityChannelCommunityPreviewIcon
StatusCommunityChannelCommunityPreviewBanner
QtObject: QtObject:
type type
@ -66,14 +83,23 @@ QtObject:
ModelRole.Url.int:"url", ModelRole.Url.int:"url",
ModelRole.Unfurled.int:"unfurled", ModelRole.Unfurled.int:"unfurled",
ModelRole.Immutable.int:"immutable", ModelRole.Immutable.int:"immutable",
ModelRole.Hostname.int:"hostname", ModelRole.Empty.int:"empty",
ModelRole.Title.int:"title", ModelRole.PreviewType.int:"previewType",
ModelRole.Description.int:"description", # Standard
ModelRole.LinkType.int:"linkType", ModelRole.StandardPreview.int:"standardPreview",
ModelRole.ThumbnailWidth.int:"thumbnailWidth", ModelRole.StandardPreviewThumbnail.int:"standardPreviewThumbnail",
ModelRole.ThumbnailHeight.int:"thumbnailHeight", # Contact
ModelRole.ThumbnailUrl.int:"thumbnailUrl", ModelRole.StatusContactPreview.int:"statusContactPreview",
ModelRole.ThumbnailDataUri.int:"thumbnailDataUri", ModelRole.StatusContactPreviewThumbnail.int:"statusContactPreviewThumbnail",
# Community
ModelRole.StatusCommunityPreview.int:"statusCommunityPreview",
ModelRole.StatusCommunityPreviewIcon.int:"statusCommunityPreviewIcon",
ModelRole.StatusCommunityPreviewBanner.int:"statusCommunityPreviewBanner",
# Channel
ModelRole.StatusCommunityChannelPreview.int:"statusCommunityChannelPreview",
ModelRole.StatusCommunityChannelCommunityPreview.int:"statusCommunityChannelCommunityPreview",
ModelRole.StatusCommunityChannelCommunityPreviewIcon.int:"statusCommunityChannelCommunityPreviewIcon",
ModelRole.StatusCommunityChannelCommunityPreviewBanner.int:"statusCommunityChannelCommunityPreviewBanner",
}.toTable }.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant = method data(self: Model, index: QModelIndex, role: int): QVariant =
@ -93,22 +119,45 @@ QtObject:
result = newQVariant(item.unfurled) result = newQVariant(item.unfurled)
of ModelRole.Immutable: of ModelRole.Immutable:
result = newQVariant(item.immutable) result = newQVariant(item.immutable)
of ModelRole.Hostname: of ModelRole.Empty:
result = newQVariant(item.linkPreview.hostname) result = newQVariant(item.linkPreview.empty())
of ModelRole.Title: of ModelRole.PreviewType:
result = newQVariant(item.linkPreview.title) result = newQVariant(item.linkPreview.previewType.int)
of ModelRole.Description: of ModelRole.StandardPreview:
result = newQVariant(item.linkPreview.description) if item.linkPreview.standardPreview != nil:
of ModelRole.LinkType: result = newQVariant(item.linkPreview.standardPreview)
result = newQVariant(item.linkPreview.linkType.int) of ModelRole.StandardPreviewThumbnail:
of ModelRole.ThumbnailWidth: if item.linkPreview.standardPreview != nil:
result = newQVariant(item.linkPreview.thumbnail.width) result = newQVariant(item.linkPreview.standardPreview.getThumbnail())
of ModelRole.ThumbnailHeight: of ModelRole.StatusContactPreview:
result = newQVariant(item.linkPreview.thumbnail.height) if item.linkPreview.statusContactPreview != nil:
of ModelRole.ThumbnailUrl: result = newQVariant(item.linkPreview.statusContactPreview)
result = newQVariant(item.linkPreview.thumbnail.url) of ModelRole.StatusContactPreviewThumbnail:
of ModelRole.ThumbnailDataUri: if item.linkPreview.statusContactPreview != nil:
result = newQVariant(item.linkPreview.thumbnail.dataUri) result = newQVariant(item.linkPreview.statusContactPreview.getIcon())
of ModelRole.StatusCommunityPreview:
if item.linkPreview.statusCommunityPreview != nil:
result = newQVariant(item.linkPreview.statusCommunityPreview)
of ModelRole.StatusCommunityPreviewIcon:
if item.linkPreview.statusCommunityPreview != nil:
result = newQVariant(item.linkPreview.statusCommunityPreview.getIcon())
of ModelRole.StatusCommunityPreviewBanner:
if item.linkPreview.statusCommunityPreview != nil:
result = newQVariant(item.linkPreview.statusCommunityPreview.getBanner())
of ModelRole.StatusCommunityChannelPreview:
if item.linkPreview.statusCommunityChannelPreview != nil:
result = newQVariant(item.linkPreview.statusCommunityChannelPreview)
of ModelRole.StatusCommunityChannelCommunityPreview:
if (let community = item.linkPreview.getChannelCommunity(); community) != nil:
result = newQVariant(community)
of ModelRole.StatusCommunityChannelCommunityPreviewIcon:
if (let community = item.linkPreview.getChannelCommunity(); community) != nil:
result = newQVariant(community.getIcon())
of ModelRole.StatusCommunityChannelCommunityPreviewBanner:
if (let community = item.linkPreview.getChannelCommunity(); community) != nil:
result = newQVariant(community.getBanner())
else:
result = newQVariant()
proc removeItemWithIndex(self: Model, ind: int) = proc removeItemWithIndex(self: Model, ind: int) =
if(ind < 0 or ind >= self.items.len): if(ind < 0 or ind >= self.items.len):
@ -220,10 +269,10 @@ QtObject:
proc getUnfuledLinkPreviews*(self: Model): seq[LinkPreview] = proc getUnfuledLinkPreviews*(self: Model): seq[LinkPreview] =
result = @[] result = @[]
for item in self.items: for item in self.items:
if item.unfurled and item.linkPreview.hostName != "": if item.unfurled and not item.linkPreview.empty():
result.add(item.linkPreview) result.add(item.linkPreview)
proc getLinks*(self: Model): seq[string] = proc getLinks*(self: Model): seq[string] =
result = @[] result = @[]
for item in self.items: for item in self.items:
result.add(item.linkPreview.url) result.add(item.linkPreview.url)

View File

@ -1,85 +1,128 @@
import json, strformat, tables import json, strformat, tables
import ./link_preview_thumbnail, ./status_link_preview, ./standard_link_preview
import ./status_contact_link_preview, ./status_community_link_preview, ./status_community_channel_link_preview
include ../../../common/json_utils include ../../../common/json_utils
type
LinkType* {.pure.} = enum
Link = 0
Image
proc toLinkType*(value: int): LinkType =
try:
return LinkType(value)
except RangeDefect:
return LinkType.Link
type type
LinkPreviewThumbnail* = object PreviewType {.pure.} = enum
width*: int NoPreview = 0
height*: int StandardPreview
url*: string StatusContactPreview
dataUri*: string StatusCommunityPreview
StatusCommunityChannelPreview
type
LinkPreview* = ref object LinkPreview* = ref object
url*: string url*: string
hostname*: string previewType*: PreviewType
title*: string standardPreview*: StandardLinkPreview
description*: string statusContactPreview*: StatusContactLinkPreview
thumbnail*: LinkPreviewThumbnail statusCommunityPreview*: StatusCommunityLinkPreview
linkType*: LinkType statusCommunityChannelPreview*: StatusCommunityChannelLinkPreview
proc delete*(self: LinkPreview) = proc delete*(self: LinkPreview) =
discard if self.standardPreview != nil:
self.standardPreview.delete
if self.statusContactPreview != nil:
self.statusContactPreview.delete
if self.statusCommunityPreview != nil:
self.statusCommunityPreview.delete
if self.statusCommunityChannelPreview != nil:
self.statusCommunityChannelPreview.delete
proc initLinkPreview*(url: string): LinkPreview = proc initLinkPreview*(url: string): LinkPreview =
result = LinkPreview() result = LinkPreview()
result.url = url result.url = url
result.previewType = PreviewType.NoPreview
proc toLinkPreviewThumbnail*(jsonObj: JsonNode): LinkPreviewThumbnail = proc toLinkPreview*(jsonObj: JsonNode, standard: bool): LinkPreview =
result = LinkPreviewThumbnail()
discard jsonObj.getProp("width", result.width)
discard jsonObj.getProp("height", result.height)
discard jsonObj.getProp("url", result.url)
discard jsonObj.getProp("dataUri", result.dataUri)
proc toLinkPreview*(jsonObj: JsonNode): LinkPreview =
result = LinkPreview() result = LinkPreview()
discard jsonObj.getProp("url", result.url) result.previewType = PreviewType.NoPreview
discard jsonObj.getProp("hostname", result.hostname)
discard jsonObj.getProp("title", result.title)
discard jsonObj.getProp("description", result.description)
discard jsonObj.getProp("hostname", result.hostname)
result.linkType = toLinkType(jsonObj["type"].getInt)
var thumbnail: JsonNode if standard:
if jsonObj.getProp("thumbnail", thumbnail): discard jsonObj.getProp("url", result.url)
result.thumbnail = toLinkPreviewThumbnail(thumbnail) result.previewType = PreviewType.StandardPreview
result.standardPreview = toStandardLinkPreview(jsonObj)
proc `$`*(self: LinkPreviewThumbnail): string = else:
result = fmt"""LinkPreviewThumbnail( discard jsonObj.getProp("url", result.url)
width: {self.width}, var node: JsonNode
height: {self.height}, if jsonObj.getProp("contact", node):
urlLength: {self.url.len}, result.previewType = PreviewType.StatusContactPreview
dataUriLength: {self.dataUri.len} result.statusContactPreview = toStatusContactLinkPreview(node)
)""" elif jsonObj.getProp("community", node):
result.previewType = PreviewType.StatusCommunityPreview
result.statusCommunityPreview = toStatusCommunityLinkPreview(node)
elif jsonObj.getProp("channel", node):
result.previewType = PreviewType.StatusCommunityChannelPreview
result.statusCommunityChannelPreview = toStatusCommunityChannelLinkPreview(node)
proc `$`*(self: LinkPreview): string = proc `$`*(self: LinkPreview): string =
let standardPreview = if self.standardPreview != nil: $self.standardPreview else: ""
let contactPreview = if self.statusContactPreview != nil: $self.statusContactPreview else: ""
let communityPreview = if self.statusCommunityPreview != nil: $self.statusCommunityPreview else: ""
let channelPreview = if self.statusCommunityChannelPreview != nil: $self.statusCommunityChannelPreview else: ""
result = fmt"""LinkPreview( result = fmt"""LinkPreview(
type: {self.linkType},
url: {self.url}, url: {self.url},
hostname: {self.hostname}, previewType: {self.previewType},
title: {self.title}, standardPreview: {standardPreview},
description: {self.description}, contactPreview: {contactPreview},
thumbnail: {self.thumbnail} communityPreview: {communityPreview},
channelPreview: {channelPreview}
)""" )"""
# Custom JSON converter to force `linkType` integer instead of string
proc `%`*(self: LinkPreview): JsonNode = proc `%`*(self: LinkPreview): JsonNode =
result = %* { result = %* {
"type": self.linkType.int,
"url": self.url, "url": self.url,
"hostname": self.hostname, "standardPreview": %self.standardPreview,
"title": self.title, "contactPreview": %self.statusContactPreview,
"description": self.description, "communityPreview": %self.statusCommunityPreview,
"thumbnail": %self.thumbnail, "channelPreview": %self.statusCommunityChannelPreview
} }
proc empty*(self: LinkPreview): bool =
case self.previewType:
of PreviewType.StandardPreview:
return self.standardPreview == nil or self.standardPreview.empty()
of PreviewType.StatusContactPreview:
return self.statusContactPreview == nil or self.statusContactPreview.empty()
of PreviewType.StatusCommunityPreview:
return self.statusCommunityPreview == nil or self.statusCommunityPreview.empty()
of PreviewType.StatusCommunityChannelPreview:
return self.statusCommunityChannelPreview == nil or self.statusCommunityChannelPreview.empty()
else:
return true
proc extractLinkPreviewsLists*(input: seq[LinkPreview]): (seq[StandardLinkPreview], seq[StatusLinkPreview]) =
var standard: seq[StandardLinkPreview]
var status: seq[StatusLinkPreview]
for preview in input:
case preview.previewType:
of PreviewType.StandardPreview:
if preview.standardPreview != nil:
preview.standardPreview.url = preview.url
standard.add(preview.standardPreview)
of PreviewType.StatusContactPreview:
let statusLinkPreview = StatusLinkPreview()
statusLinkPreview.url = preview.url
statusLinkPreview.contact = preview.statusContactPreview
status.add(statusLinkPreview)
of PreviewType.StatusCommunityPreview:
let statusLinkPreview = StatusLinkPreview()
statusLinkPreview.url = preview.url
statusLinkPreview.community = preview.statusCommunityPreview
status.add(statusLinkPreview)
of PreviewType.StatusCommunityChannelPreview:
let statusLinkPreview = StatusLinkPreview()
statusLinkPreview.url = preview.url
statusLinkPreview.channel = preview.statusCommunityChannelPreview
status.add(statusLinkPreview)
else:
discard
return (standard, status)
proc getChannelCommunity*(self: LinkPreview): StatusCommunityLinkPreview =
if self.statusCommunityChannelPreview == nil:
return nil
return self.statusCommunityChannelPreview.getCommunity()

View File

@ -0,0 +1,84 @@
import json, strformat, NimQml, chronicles
include ../../../common/json_utils
QtObject:
type LinkPreviewThumbnail* = ref object of QObject
width: int
height: int
url: string
dataUri: string
proc setup*(self: LinkPreviewThumbnail) =
self.QObject.setup()
proc delete*(self: LinkPreviewThumbnail) =
self.QObject.delete()
proc update*(self: LinkPreviewThumbnail, width: int, height: int, url: string, dataUri: string) =
self.width = width
self.height = height
self.url = url
self.dataUri = dataUri
proc copy*(self: LinkPreviewThumbnail, other: LinkPreviewThumbnail) =
if other != nil:
self.update(other.width, other.height, other.url, other.dataUri)
else:
self.update(0, 0, "", "")
proc newLinkPreviewThumbnail*(width: int = 0, height: int = 0, url: string = "", dataUri: string = ""): LinkPreviewThumbnail =
new(result, delete)
result.setup()
result.update(width, height, url, dataUri)
proc widthChanged*(self: LinkPreviewThumbnail) {.signal.}
proc getWidth*(self: LinkPreviewThumbnail): int {.slot.} =
result = self.width
QtProperty[int] width:
read = getWidth
notify = widthChanged
proc heightChanged*(self: LinkPreviewThumbnail) {.signal.}
proc getHeight*(self: LinkPreviewThumbnail): int {.slot.} =
result = self.height
QtProperty[int] height:
read = getHeight
notify = heightChanged
proc urlChanged*(self: LinkPreviewThumbnail) {.signal.}
proc getUrl*(self: LinkPreviewThumbnail): string {.slot.} =
result = self.url
QtProperty[string] url:
read = getUrl
notify = urlChanged
proc dataUriChanged*(self: LinkPreviewThumbnail) {.signal.}
proc getDataUri*(self: LinkPreviewThumbnail): string {.slot.} =
result = self.dataUri
QtProperty[string] dataUri:
read = getDataUri
notify = dataUriChanged
proc toLinkPreviewThumbnail*(jsonObj: JsonNode): LinkPreviewThumbnail =
result = LinkPreviewThumbnail()
discard jsonObj.getProp("width", result.width)
discard jsonObj.getProp("height", result.height)
discard jsonObj.getProp("url", result.url)
discard jsonObj.getProp("dataUri", result.dataUri)
proc `$`*(self: LinkPreviewThumbnail): string =
result = fmt"""LinkPreviewThumbnail(
width: {self.width},
height: {self.height},
urlLength: {self.url.len},
dataUriLength: {self.dataUri.len}
)"""
proc `%`*(self: LinkPreviewThumbnail): JsonNode =
result = %*{
"width": self.width,
"height": self.height,
"url": self.url,
"dataUri": self.dataUri
}

View File

@ -1,6 +1,6 @@
{.used.} {.used.}
import json, strutils import json, strutils, chronicles
import ../../../common/types import ../../../common/types
import link_preview import link_preview
@ -263,7 +263,12 @@ proc toMessageDto*(jsonObj: JsonNode): MessageDto =
var linkPreviewsArr: JsonNode var linkPreviewsArr: JsonNode
if jsonObj.getProp("linkPreviews", linkPreviewsArr): if jsonObj.getProp("linkPreviews", linkPreviewsArr):
for element in linkPreviewsArr.getElems(): for element in linkPreviewsArr.getElems():
result.linkPreviews.add(element.toLinkPreview()) result.linkPreviews.add(element.toLinkPreview(true))
var statusLinkPreviewsArr: JsonNode
if jsonObj.getProp("statusLinkPreviews", statusLinkPreviewsArr):
for element in statusLinkPreviewsArr.getElems():
result.linkPreviews.add(element.toLinkPreview(false))
var parsedTextArr: JsonNode var parsedTextArr: JsonNode
if(jsonObj.getProp("parsedText", parsedTextArr) and parsedTextArr.kind == JArray): if(jsonObj.getProp("parsedText", parsedTextArr) and parsedTextArr.kind == JArray):

View File

@ -0,0 +1,114 @@
import json, strformat, NimQml
import ./link_preview_thumbnail
include ../../../common/json_utils
type
LinkType* {.pure.} = enum
Link = 0
Image
proc toLinkType*(value: int): LinkType =
try:
return LinkType(value)
except RangeDefect:
return LinkType.Link
QtObject:
type StandardLinkPreview* = ref object of QObject
url*: string # this property is set manually and only used in`toJSON` conversion
hostname: string
title: string
description: string
linkType: LinkType
thumbnail: LinkPreviewThumbnail
proc setup*(self: StandardLinkPreview) =
self.QObject.setup
self.thumbnail = newLinkPreviewThumbnail()
proc delete*(self: StandardLinkPreview) =
self.QObject.delete
self.thumbnail.delete
proc newStandardLinkPreview*(hostname: string, title: string, description: string, thumbnail: LinkPreviewThumbnail, linkType: LinkType): StandardLinkPreview =
new(result, delete)
result.setup()
result.hostname = hostname
result.title = title
result.description = description
result.linkType = linkType
result.thumbnail.copy(thumbnail)
proc hostnameChanged*(self: StandardLinkPreview) {.signal.}
proc getHostname*(self: StandardLinkPreview): string {.slot.} =
result = self.hostname
QtProperty[string] hostname:
read = getHostname
notify = hostnameChanged
proc titleChanged*(self: StandardLinkPreview) {.signal.}
proc getTitle*(self: StandardLinkPreview): string {.slot.} =
result = self.title
QtProperty[string] title:
read = getTitle
notify = titleChanged
proc descriptionChanged*(self: StandardLinkPreview) {.signal.}
proc getDescription*(self: StandardLinkPreview): string {.slot.} =
result = self.description
QtProperty[string] description:
read = getDescription
notify = descriptionChanged
proc linkTypeChanged*(self: StandardLinkPreview) {.signal.}
proc getLinkType*(self: StandardLinkPreview): int {.slot.} =
result = self.linkType.int
QtProperty[int] linkType:
read = getLinkType
notify = linkTypeChanged
proc getThumbnail*(self: StandardLinkPreview): LinkPreviewThumbnail =
result = self.thumbnail
proc toStandardLinkPreview*(jsonObj: JsonNode): StandardLinkPreview =
var hostname: string
var title: string
var description: string
var linkType: LinkType
var thumbnail: LinkPreviewThumbnail
discard jsonObj.getProp("hostname", hostname)
discard jsonObj.getProp("title", title)
discard jsonObj.getProp("description", description)
linkType = toLinkType(jsonObj["type"].getInt)
var thumbnailJson: JsonNode
if jsonObj.getProp("thumbnail", thumbnailJson):
thumbnail = toLinkPreviewThumbnail(thumbnailJson)
result = newStandardLinkPreview(hostname, title, description, thumbnail, linkType)
proc `$`*(self: StandardLinkPreview): string =
result = fmt"""StandardLinkPreview(
type: {self.linkType},
hostname: {self.hostname},
title: {self.title},
description: {self.description},
thumbnail: {self.thumbnail}
)"""
# Custom JSON converter to force `linkType` integer instead of string
proc `%`*(self: StandardLinkPreview): JsonNode =
result = %* {
"url": self.url,
"type": self.linkType.int,
"hostname": self.hostname,
"title": self.title,
"description": self.description,
"thumbnail": %self.thumbnail,
}
proc empty*(self: StandardLinkPreview): bool =
return self.hostname.len == 0

View File

@ -0,0 +1,95 @@
import json, strformat, NimQml, chronicles
import link_preview_thumbnail
import status_community_link_preview
include ../../../common/json_utils
QtObject:
type StatusCommunityChannelLinkPreview* = ref object of QObject
channelUuid: string
emoji: string
displayName: string
description: string
color: string
community: StatusCommunityLinkPreview
proc setup*(self: StatusCommunityChannelLinkPreview) =
self.QObject.setup()
proc delete*(self: StatusCommunityChannelLinkPreview) =
self.QObject.delete()
self.community.delete()
proc channelUuidChanged*(self: StatusCommunityChannelLinkPreview) {.signal.}
proc getChannelUuid*(self: StatusCommunityChannelLinkPreview): string {.slot.} =
return self.channelUuid
QtProperty[string] channelUuid:
read = getChannelUuid
notify = channelUuidChanged
proc emojiChanged*(self: StatusCommunityChannelLinkPreview) {.signal.}
proc getEmoji*(self: StatusCommunityChannelLinkPreview): string {.slot.} =
return self.emoji
QtProperty[string] emoji:
read = getEmoji
notify = emojiChanged
proc displayNameChanged*(self: StatusCommunityChannelLinkPreview) {.signal.}
proc getDisplayName*(self: StatusCommunityChannelLinkPreview): string {.slot.} =
return self.displayName
QtProperty[string] displayName:
read = getDisplayName
notify = displayNameChanged
proc descriptionChanged*(self: StatusCommunityChannelLinkPreview) {.signal.}
proc getDescription*(self: StatusCommunityChannelLinkPreview): string {.slot.} =
return self.description
QtProperty[string] description:
read = getDescription
notify = descriptionChanged
proc colorChanged*(self: StatusCommunityChannelLinkPreview) {.signal.}
proc getColor*(self: StatusCommunityChannelLinkPreview): string {.slot.} =
return self.color
QtProperty[string] color:
read = getColor
notify = colorChanged
proc getCommunity*(self: StatusCommunityChannelLinkPreview): StatusCommunityLinkPreview =
return self.community
proc toStatusCommunityChannelLinkPreview*(jsonObj: JsonNode): StatusCommunityChannelLinkPreview =
new(result, delete)
result.setup()
discard jsonObj.getProp("channelUuid", result.channelUuid)
discard jsonObj.getProp("emoji", result.emoji)
discard jsonObj.getProp("displayName", result.displayName)
discard jsonObj.getProp("description", result.description)
discard jsonObj.getProp("color", result.color)
var communityJsonNode: JsonNode
if jsonObj.getProp("community", communityJsonNode):
result.community = toStatusCommunityLinkPreview(communityJsonNode)
proc `$`*(self: StatusCommunityChannelLinkPreview): string =
return fmt"""StatusCommunityChannelLinkPreview(
channelUuid: {self.channelUuid},
emoji: {self.emoji},
displayName: {self.displayName},
description: {self.description},
color: {self.color},
community: {self.community}
)"""
proc `%`*(self: StatusCommunityChannelLinkPreview): JsonNode =
return %* {
"channelUuid": self.channelUuid,
"emoji": self.emoji,
"displayName": self.displayName,
"description": self.description,
"color": self.color,
"community": self.community
}
proc empty*(self: StatusCommunityChannelLinkPreview): bool =
return self.channelUUID.len == 0

View File

@ -0,0 +1,114 @@
import json, strformat, NimQml, chronicles
import link_preview_thumbnail
include ../../../common/json_utils
QtObject:
type StatusCommunityLinkPreview* = ref object of QObject
communityID: string
displayName: string
description: string
membersCount: int
color: string
icon: LinkPreviewThumbnail
banner: LinkPreviewThumbnail
proc setup*(self: StatusCommunityLinkPreview) =
self.QObject.setup()
self.icon = newLinkPreviewThumbnail()
self.banner = newLinkPreviewThumbnail()
proc delete*(self: StatusCommunityLinkPreview) =
self.QObject.delete()
self.icon.delete()
self.banner.delete()
proc communityIdChanged*(self: StatusCommunityLinkPreview) {.signal.}
proc getCommunityId*(self: StatusCommunityLinkPreview): string {.slot.} =
result = self.communityID
QtProperty[string] communityId:
read = getCommunityId
notify = communityIdChanged
proc displayNameChanged*(self: StatusCommunityLinkPreview) {.signal.}
proc getDisplayName*(self: StatusCommunityLinkPreview): string {.slot.} =
result = self.displayName
QtProperty[string] displayName:
read = getDisplayName
notify = displayNameChanged
proc descriptionChanged*(self: StatusCommunityLinkPreview) {.signal.}
proc getDescription*(self: StatusCommunityLinkPreview): string {.slot.} =
result = self.description
QtProperty[string] description:
read = getDescription
notify = descriptionChanged
proc membersCountChanged*(self: StatusCommunityLinkPreview) {.signal.}
proc getMembersCount*(self: StatusCommunityLinkPreview): int {.slot.} =
result = int(self.membersCount)
QtProperty[int] membersCount:
read = getMembersCount
notify = membersCountChanged
proc colorChanged*(self: StatusCommunityLinkPreview) {.signal.}
proc getColor*(self: StatusCommunityLinkPreview): string {.slot.} =
result = self.color
QtProperty[string] color:
read = getColor
notify = colorChanged
proc getIcon*(self: StatusCommunityLinkPreview): LinkPreviewThumbnail =
result = self.icon
proc getBanner*(self: StatusCommunityLinkPreview): LinkPreviewThumbnail =
result = self.banner
proc toStatusCommunityLinkPreview*(jsonObj: JsonNode): StatusCommunityLinkPreview =
new(result, delete)
result.setup()
var icon: LinkPreviewThumbnail
var banner: LinkPreviewThumbnail
discard jsonObj.getProp("communityId", result.communityID)
discard jsonObj.getProp("displayName", result.displayName)
discard jsonObj.getProp("description", result.description)
discard jsonObj.getProp("membersCount", result.membersCount)
discard jsonObj.getProp("color", result.color)
var iconJson: JsonNode
if jsonObj.getProp("icon", iconJson):
icon = toLinkPreviewThumbnail(iconJson)
var bannerJson: JsonNode
if jsonObj.getProp("banner", bannerJson):
banner = toLinkPreviewThumbnail(bannerJson)
result.icon.copy(icon)
result.banner.copy(banner)
proc `$`*(self: StatusCommunityLinkPreview): string =
result = fmt"""StatusCommunityLinkPreview(
communityId: {self.communityID},
displayName: {self.displayName},
description: {self.description},
membersCount: {self.membersCount},
color: {self.color},
icon: {self.icon},
banner: {self.banner}
)"""
proc `%`*(self: StatusCommunityLinkPreview): JsonNode =
result = %* {
"communityID": self.communityID,
"displayName": self.displayName,
"description": self.description,
"membersCount": self.membersCount,
"color": self.color,
"icon": self.icon,
"banner": self.banner
}
proc empty*(self: StatusCommunityLinkPreview): bool =
return self.communityID.len == 0

View File

@ -0,0 +1,87 @@
import json, strformat, NimQml, chronicles
import link_preview_thumbnail
include ../../../common/json_utils
QtObject:
type StatusContactLinkPreview* = ref object of QObject
publicKey: string
displayName: string
description: string
icon: LinkPreviewThumbnail
proc setup*(self: StatusContactLinkPreview) =
self.QObject.setup()
self.icon = newLinkPreviewThumbnail()
proc delete*(self: StatusContactLinkPreview) =
self.QObject.delete()
self.icon.delete()
proc newStatusContactLinkPreview*(publicKey: string, displayName: string, description: string, icon: LinkPreviewThumbnail): StatusContactLinkPreview =
new(result, delete)
result.setup()
result.publicKey = publicKey
result.displayName = displayName
result.description = description
result.icon.copy(icon)
proc publicKeyChanged*(self: StatusContactLinkPreview) {.signal.}
proc getPublicKey*(self: StatusContactLinkPreview): string {.slot.} =
result = self.publicKey
QtProperty[string] publicKey:
read = getPublicKey
notify = publicKeyChanged
proc displayNameChanged*(self: StatusContactLinkPreview) {.signal.}
proc getDisplayName*(self: StatusContactLinkPreview): string {.slot.} =
result = self.displayName
QtProperty[string] displayName:
read = getDisplayName
notify = displayNameChanged
proc descriptionChanged*(self: StatusContactLinkPreview) {.signal.}
proc getDescription*(self: StatusContactLinkPreview): string {.slot.} =
result = self.description
QtProperty[string] description:
read = getDescription
notify = descriptionChanged
proc getIcon*(self: StatusContactLinkPreview): LinkPreviewThumbnail =
result = self.icon
proc toStatusContactLinkPreview*(jsonObj: JsonNode): StatusContactLinkPreview =
var publicKey: string
var displayName: string
var description: string
var icon: LinkPreviewThumbnail
discard jsonObj.getProp("publicKey", publicKey)
discard jsonObj.getProp("displayName", displayName)
discard jsonObj.getProp("description", description)
var iconJson: JsonNode
if jsonObj.getProp("icon", iconJson):
icon = toLinkPreviewThumbnail(iconJson)
result = newStatusContactLinkPreview(publicKey, displayName, description, icon)
proc `$`*(self: StatusContactLinkPreview): string =
result = fmt"""StatusContactLinkPreview(
publicKey: {self.publicKey},
displayName: {self.displayName},
description: {self.description},
icon: {self.icon}
)"""
proc `%`*(self: StatusContactLinkPreview): JsonNode =
return %* {
"publicKey": self.publicKey,
"displayName": self.displayName,
"description": self.description,
"icon": self.icon
}
proc empty*(self: StatusContactLinkPreview): bool =
return self.publicKey.len == 0

View File

@ -0,0 +1,41 @@
import json, strformat, NimQml, chronicles
import link_preview_thumbnail
import status_contact_link_preview
import status_community_link_preview
import status_community_channel_link_preview
include ../../../common/json_utils
type StatusLinkPreview* = ref object
url*: string
contact*: StatusContactLinkPreview
community*: StatusCommunityLinkPreview
channel*: StatusCommunityChannelLinkPreview
proc toStatusLinkPreview*(jsonObj: JsonNode): StatusLinkPreview =
result = StatusLinkPreview()
discard jsonObj.getProp("url", result.url)
var contact: JsonNode
if jsonObj.getProp("contact", contact):
result.contact = toStatusContactLinkPreview(contact)
var community: JsonNode
if jsonObj.getProp("community", community):
result.community = toStatusCommunityLinkPreview(contact)
var channel: JsonNode
if jsonObj.getProp("channel", channel):
result.channel = toStatusCommunityChannelLinkPreview(contact)
proc `%`*(self: StatusLinkPreview): JsonNode =
var obj = %*{
"url": self.url
}
if self.contact != nil:
obj["contact"] = %*self.contact
if self.community != nil:
obj["community"] = %*self.community
if self.channel != nil:
obj["channel"] = %*self.channel
return obj

View File

@ -18,6 +18,7 @@ import ../chat/dto/chat as chat_dto
import ./dto/pinned_message_update as pinned_msg_update_dto import ./dto/pinned_message_update as pinned_msg_update_dto
import ./dto/removed_message as removed_msg_dto import ./dto/removed_message as removed_msg_dto
import ./dto/link_preview import ./dto/link_preview
import ./dto/status_link_preview
import ./message_cursor import ./message_cursor
import ../../common/message as message_common import ../../common/message as message_common
@ -863,11 +864,20 @@ QtObject:
if responseObj.getProp("requestedUrls", requestedUrlsArr): if responseObj.getProp("requestedUrls", requestedUrlsArr):
requestedUrls = map(requestedUrlsArr.getElems(), proc(x: JsonNode): string = x.getStr) requestedUrls = map(requestedUrlsArr.getElems(), proc(x: JsonNode): string = x.getStr)
var linkPreviewsArr: JsonNode let unfurlResponse = responseObj["response"]
var linkPreviews: Table[string, LinkPreview] var linkPreviews: Table[string, LinkPreview]
if responseObj.getProp("response", linkPreviewsArr): var linkPreviewsArr: JsonNode
var statusLinkPreviewsArr: JsonNode
if unfurlResponse.getProp("linkPreviews", linkPreviewsArr):
for element in linkPreviewsArr.getElems(): for element in linkPreviewsArr.getElems():
let linkPreview = element.toLinkPreview() let linkPreview = element.toLinkPreview(true)
linkPreviews[linkPreview.url] = linkPreview
if unfurlResponse.getProp("statusLinkPreviews", statusLinkPreviewsArr):
for element in statusLinkPreviewsArr.getElems():
let linkPreview = element.toLinkPreview(false)
linkPreviews[linkPreview.url] = linkPreview linkPreviews[linkPreview.url] = linkPreview
for url in requestedUrls: for url in requestedUrls:

View File

@ -3,6 +3,11 @@ import core, ../app_service/common/utils
import response_type import response_type
import interpret/cropped_image import interpret/cropped_image
import ../app_service/service/message/dto/link_preview import ../app_service/service/message/dto/link_preview
import ../app_service/service/message/dto/standard_link_preview
import ../app_service/service/message/dto/status_link_preview
import ../app_service/service/message/dto/status_contact_link_preview
import ../app_service/service/message/dto/status_community_link_preview
import ../app_service/service/message/dto/status_community_channel_link_preview
export response_type export response_type
@ -66,6 +71,7 @@ proc sendChatMessage*(
stickerHash: string = "", stickerHash: string = "",
stickerPack: string = "0", stickerPack: string = "0",
): RpcResponse[JsonNode] {.raises: [Exception].} = ): RpcResponse[JsonNode] {.raises: [Exception].} =
let (standardLinkPreviews, statusLinkPreviews) = extractLinkPreviewsLists(linkPreviews)
result = callPrivateRPC("sendChatMessage".prefix, %* [ result = callPrivateRPC("sendChatMessage".prefix, %* [
{ {
"chatId": chatId, "chatId": chatId,
@ -78,7 +84,8 @@ proc sendChatMessage*(
}, },
"contentType": contentType, "contentType": contentType,
"communityId": communityId, "communityId": communityId,
"linkPreviews": linkPreviews "linkPreviews": standardLinkPreviews,
"statusLinkPreviews": statusLinkPreviews
} }
]) ])

View File

@ -4,11 +4,121 @@ import QtQuick.Layouts 1.15
import shared.views.chat 1.0 import shared.views.chat 1.0
SplitView { SplitView {
ListModel {
id: mockedLinkPreviewModel
// Create the model dynamically, because `ListElement` doesnt suppport nested elements
Component.onCompleted: {
const emptyObject = {
"unfurled": true,
"immutable": false,
"empty": false,
"url": "https://www.youtube.com/watch?v=9bZkp7q19f0",
"previewType": 1,
"standardPreview": {
"hostname": "www.youtube.com",
"title": "PSY - GANGNAM STYLE(강남스타일) M/V",
"description": "PSY - I LUV IT M/V @ https://youtu.be/Xvjnoagk6GU PSY - New Face M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp...",
"linkType": 0,
},
"standardPreviewThumbnail": {
"width": 480,
"height": 360,
"url": "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg",
"dataUri": "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg",
},
"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.standardLinkType = 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 { Pane {
id: messageViewWrapper id: messageViewWrapper
SplitView.fillWidth: true SplitView.fillWidth: true
SplitView.fillHeight: 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 { LinksMessageView {
id: linksMessageView id: linksMessageView
@ -16,57 +126,7 @@ SplitView {
store: {} store: {}
messageStore: {} messageStore: {}
linkPreviewModel: ListModel { linkPreviewModel: mockedLinkPreviewModel
id: linkPreviewModel
ListElement {
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
unfurled: true
hostname: "www.youtube.com"
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
description: "PSY - I LUV IT M/V @ https://youtu.be/Xvjnoagk6GU PSY - New Face M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
linkType: 0 // 0 = link, 1 = image
thumbnailWidth: 480
thumbnailHeight: 360
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
}
ListElement {
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
unfurled: true
hostname: "www.youtube.com"
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
description: "PSY - I LUV IT M/V @ https://youtu.be/Xvjnoagk6GU PSY - New Face M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
linkType: 0 // 0 = link, 1 = image
thumbnailWidth: 480
thumbnailHeight: 360
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
}
ListElement {
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
unfurled: true
hostname: "www.youtube.com"
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
description: "PSY - I LUV IT M/V @ https://youtu.be/Xvjnoagk6GU PSY - New Face M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
linkType: 0 // 0 = link, 1 = image
thumbnailWidth: 480
thumbnailHeight: 360
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
}
ListElement {
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
unfurled: true
hostname: "www.youtube.com"
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
description: "PSY - I LUV IT M/V @ https://youtu.be/Xvjnoagk6GU PSY - New Face M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
linkType: 0 // 0 = link, 1 = image
thumbnailWidth: 480
thumbnailHeight: 360
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
}
}
localUnfurlLinks: {} localUnfurlLinks: {}
isCurrentUser: true isCurrentUser: true
@ -94,4 +154,4 @@ SplitView {
} }
} }
} }
} }

View File

@ -0,0 +1,133 @@
import QtQuick 2.15
import StatusQ.Core 0.1
import utils 1.0
StatusBaseText {
id: root
required property bool unfurled
required property bool empty
required property string url
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
wrapMode: Text.WordWrap
function getThumbnailString(thumbnail) {
const thumbnailWidth = thumbnail ? thumbnail.width : ""
const thumbnailHeight = thumbnail ? thumbnail.height : ""
const thumbnailUrl = thumbnail ? thumbnail.url : ""
const thumbnailDataUri = thumbnail ? thumbnail.dataUri : ""
return `(${thumbnailWidth}*${thumbnailHeight}, url: ${thumbnailUrl.length} symbols, data: ${thumbnailDataUri.length} symbols)`
}
function getStandardPreviewString(preview, thumbnail) {
const hostname = standardPreview ? standardPreview.hostname : ""
const title = standardPreview ? standardPreview.title : ""
const description = standardPreview ? standardPreview.description : ""
const thumbnailInfo = getThumbnailString(thumbnail)
return `(hostname: ${hostname}): ${title}\n` +
`description: ${description}\n` +
`thumbnail: ${thumbnailInfo}`
}
function getStatusContactPreviewString(preview, thumbnail) {
const publicKey = preview ? preview.publicKey : ""
const displayName = preview ? preview.displayName : ""
const description = preview ? preview.description : ""
const thumbnailInfo = getThumbnailString(thumbnail)
return `(publicKey: ${publicKey})\n` +
`displayName: ${displayName}\n` +
`description: ${description}\n` +
`icon: ${thumbnailInfo}`
}
function getStatusCommunityPreviewString(preview, icon, banner) {
const communityId = preview ? preview.communityId : ""
const displayName = preview ? preview.displayName : ""
const description = preview ? preview.description : ""
const membersCount = preview ? preview.membersCount : ""
const color = preview ? preview.color : ""
const iconInfo = getThumbnailString(icon)
const bannerInfo = getThumbnailString(banner)
return `communityId: ${communityId}\n` +
`displayName: ${displayName}\n` +
`description: ${description}\n` +
`membersCount: ${membersCount}\n` +
`color: ${color}\n` +
`icon: ${iconInfo}\n` +
`banner: ${bannerInfo}`
}
function getStatusChannelPreviewString(channel, community, communityIcon, communityBanner) {
const channelUuid = channel ? channel.channelUuid : ""
const displayName = channel ? channel.displayName : ""
const description = channel ? channel.description : ""
const emoji = channel ? channel.emoji : ""
const color = channel ? channel.color : ""
const communityInfo = getStatusCommunityPreviewString(community, communityIcon, communityBanner)
return `channelUuid: ${channelUuid}\n` +
`displayName: ${displayName}\n` +
`description: ${description}\n` +
`emoji: ${emoji}\n` +
`color: ${color}\n` +
`- communityInfo: \n${communityInfo}`
}
function linkPreviewTypeString(t) {
switch (t) {
case Constants.LinkPreviewType.NoPreview:
return "NoPreview"
case Constants.LinkPreviewType.Standard:
return "Standard"
case Constants.LinkPreviewType.StatusContact:
return "StatusContact"
case Constants.LinkPreviewType.StatusCommunity:
return "StatusCommunity"
case Constants.LinkPreviewType.StatusCommunityChannel:
return "StatusCommunityChannel"
}
return "???"
}
text: {
const stateEmoji = unfurled ? (empty ? '❌' : '✅') : '👀'
let previewString = ""
switch (previewType) {
case Constants.LinkPreviewType.Standard:
previewString = getStandardPreviewString(standardPreview,
standardPreviewThumbnail)
break
case Constants.LinkPreviewType.StatusContact:
previewString = getStatusContactPreviewString(statusContactPreview,
statusContactPreviewThumbnail)
break
case Constants.LinkPreviewType.StatusCommunity:
previewString = getStatusCommunityPreviewString(statusCommunityPreview,
statusCommunityPreviewIcon,
statusCommunityPreviewBanner)
break
case Constants.LinkPreviewType.StatusCommunityChannel:
previewString = getStatusChannelPreviewString(statusCommunityChannelPreview,
statusCommunityChannelCommunityPreview,
statusCommunityChannelCommunityPreviewIcon,
statusCommunityChannelCommunityPreviewBanner)
break
}
return `${stateEmoji} ${linkPreviewTypeString(previewType)} ${url}\n${previewString}`
}
}

View File

@ -101,32 +101,54 @@ Control {
model: d.filteredModel model: d.filteredModel
delegate: LinkPreviewMiniCard { delegate: LinkPreviewMiniCard {
// Model properties // Model properties
required property string title
required property string url
required property bool unfurled
required property bool immutable
required property string hostname
required property string description
required property int linkType
required property int thumbnailWidth
required property int thumbnailHeight
required property string thumbnailUrl
required property string thumbnailDataUri
required property int index 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 Layout.preferredHeight: 64
titleStr: title titleStr: standardPreview ? standardPreview.title : statusContactPreview ? statusContactPreview.displayName : ""
domain: hostname //TODO: use domain when available domain: standardPreview ? standardPreview.hostname : "" //TODO: use domain when available
favIconUrl: "" //TODO: use favicon when available favIconUrl: "" //TODO: use favicon when available
communityName: "" //TODO: add community info when available communityName: statusCommunityPreview ? statusCommunityPreview.displayName : ""
channelName: "" //TODO: add community info when available channelName: statusCommunityChannelPreview ? statusCommunityChannelPreview.displayName : ""
thumbnailImageUrl: thumbnailUrl.length > 0 ? thumbnailUrl : thumbnailDataUri thumbnailImageUrl: thumbnailUrl.length > 0 ? thumbnailUrl : thumbnailDataUri
type: linkType === 0 ? LinkPreviewMiniCard.Type.Link : LinkPreviewMiniCard.Type.Image type: getCardType(previewType, standardPreview)
previewState: unfurled && hostname != "" ? LinkPreviewMiniCard.State.Loaded : previewState: unfurled && !empty ? LinkPreviewMiniCard.State.Loaded :
unfurled && hostname === "" ? LinkPreviewMiniCard.State.LoadingFailed : unfurled && empty ? LinkPreviewMiniCard.State.LoadingFailed :
!unfurled ? LinkPreviewMiniCard.State.Loading : LinkPreviewMiniCard.State.Invalid !unfurled ? LinkPreviewMiniCard.State.Loading : LinkPreviewMiniCard.State.Invalid
onClose: root.dismissLinkPreview(d.filteredModel.mapToSource(index)) onClose: root.dismissLinkPreview(d.filteredModel.mapToSource(index))

View File

@ -21,6 +21,7 @@ CalloutCard {
} }
enum Type { enum Type {
Unknown = 0,
Link, Link,
Image, Image,
Community, Community,
@ -28,6 +29,30 @@ CalloutCard {
User User
} }
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 titleStr
required property string domain required property string domain
required property string communityName required property string communityName

View File

@ -18,6 +18,7 @@ InformationTag 1.0 InformationTag.qml
InformationTile 1.0 InformationTile.qml InformationTile 1.0 InformationTile.qml
Input 1.0 Input.qml Input 1.0 Input.qml
LoadingTokenDelegate 1.0 LoadingTokenDelegate.qml LoadingTokenDelegate 1.0 LoadingTokenDelegate.qml
LinkPreviewDebugView 1.0 LinkPreviewDebugView.qml
RadioButtonSelector 1.0 RadioButtonSelector.qml RadioButtonSelector 1.0 RadioButtonSelector.qml
RecipientSelector 1.0 RecipientSelector.qml RecipientSelector 1.0 RecipientSelector.qml
SearchBox 1.0 SearchBox.qml SearchBox 1.0 SearchBox.qml

View File

@ -7,6 +7,7 @@ import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1 import StatusQ.Controls 0.1
import StatusQ.Components 0.1 import StatusQ.Components 0.1
import shared.controls 1.0
import shared.status 1.0 import shared.status 1.0
import shared.panels 1.0 import shared.panels 1.0
import shared.stores 1.0 import shared.stores 1.0
@ -89,44 +90,90 @@ Flow {
model: root.linkPreviewModel model: root.linkPreviewModel
delegate: Loader { delegate: Loader {
id: linkMessageLoader id: linkMessageLoader
// properties from the model // properties from the model
required property string url
required property bool unfurled required property bool unfurled
required property string hostname required property bool empty
required property string title required property string url
required property string description required property bool immutable
required property int linkType required property int previewType
required property int thumbnailWidth required property var standardPreview
required property int thumbnailHeight required property var standardPreviewThumbnail
required property string thumbnailUrl required property var statusContactPreview
required property string thumbnailDataUri 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 : ""
property bool animated: false property bool animated: false
asynchronous: true asynchronous: true
active: unfurled && hostname != "" active: unfurled && !empty
sourceComponent: LinkPreviewCard { StateGroup {
id: unfurledLink //Using StateGroup as a warkardound for https://bugreports.qt.io/browse/QTBUG-47796
leftTail: !root.isCurrentUser states: [
State {
bannerImageSource: thumbnailUrl.length > 0 ? thumbnailUrl : thumbnailDataUri name: "standardLinkPreview"
title: parent.title when: linkMessageLoader.previewType === Constants.LinkPreviewType.Standard
description: parent.description PropertyChanges { target: linkMessageLoader; sourceComponent: standardLinkPreviewCard }
footer: hostname },
onClicked: State {
(mouse) => { name: "statusContactLinkPreview"
switch (mouse.button) { when: linkMessageLoader.previewType === Constants.LinkPreviewType.StatusContact
case Qt.RightButton: PropertyChanges { target: linkMessageLoader; sourceComponent: unfurledProfileLinkComponent }
root.imageClicked(unfurledLink, mouse, "", url) // request a dumb context menu with just "copy/open link" items
break
default:
Global.openLinkWithConfirmation(url, hostname)
break
}
} }
highlight: root.highlightLink === url ]
onHoveredChanged: linksRepeater.hoveredUrl = hovered ? url : "" }
}
}
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 : ""
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)
} }
} }
} }
@ -134,15 +181,12 @@ Flow {
//TODO: Remove this once we have gif support in new unfurling flow //TODO: Remove this once we have gif support in new unfurling flow
Component { Component {
id: unfurledImageComponent id: unfurledImageComponent
CalloutCard { CalloutCard {
implicitWidth: linkImage.width implicitWidth: linkImage.width
implicitHeight: linkImage.height implicitHeight: linkImage.height
leftTail: !root.isCurrentUser leftTail: !root.isCurrentUser
StatusChatImageLoader { StatusChatImageLoader {
id: linkImage id: linkImage
readonly property bool globalAnimationEnabled: root.messageStore.playAnimation readonly property bool globalAnimationEnabled: root.messageStore.playAnimation
readonly property string urlLink: link readonly property string urlLink: link
property bool localAnimationEnabled: true property bool localAnimationEnabled: true
@ -195,15 +239,11 @@ Flow {
} }
} }
} }
// Code below can be dropped when New unfurling flow suppports GIFs. // Code below can be dropped when New unfurling flow suppports GIFs.
Component { Component {
id: invitationBubble id: invitationBubble
InvitationBubbleView { InvitationBubbleView {
property var invitationData: root.store.getLinkDataForStatusLinks(link) property var invitationData: root.store.getLinkDataForStatusLinks(link)
store: root.store store: root.store
communityId: invitationData && invitationData.communityData ? invitationData.communityData.communityId : "" communityId: invitationData && invitationData.communityData ? invitationData.communityData.communityId : ""
communityData: invitationData && invitationData.communityData ? invitationData.communityData : null communityData: invitationData && invitationData.communityData ? invitationData.communityData : null
@ -214,7 +254,6 @@ Flow {
if (!invitationData) if (!invitationData)
linksModel.remove(index) linksModel.remove(index)
} }
Connections { Connections {
enabled: !!invitationData && invitationData.fetching enabled: !!invitationData && invitationData.fetching
target: root.store.communitiesModuleInst target: root.store.communitiesModuleInst
@ -225,10 +264,8 @@ Flow {
} }
} }
} }
QtObject { QtObject {
id: d id: d
readonly property string uuid: Utils.uuid() readonly property string uuid: Utils.uuid()
readonly property string whiteListedImgExtensions: Constants.acceptedImageExtensions.toString() readonly property string whiteListedImgExtensions: Constants.acceptedImageExtensions.toString()
readonly property string whiteListedUrls: JSON.stringify(localAccountSensitiveSettings.whitelistedUnfurlingSites) readonly property string whiteListedUrls: JSON.stringify(localAccountSensitiveSettings.whitelistedUnfurlingSites)
@ -241,18 +278,14 @@ Flow {
whiteListedImgExtensions, whiteListedImgExtensions,
localAccountSensitiveSettings.displayChatImages) localAccountSensitiveSettings.displayChatImages)
} }
onGetLinkPreviewDataIdChanged: { onGetLinkPreviewDataIdChanged: {
linkFetchConnections.enabled = root.localUnfurlLinks !== "" linkFetchConnections.enabled = root.localUnfurlLinks !== ""
} }
} }
Connections { Connections {
id: linkFetchConnections id: linkFetchConnections
enabled: false enabled: false
target: root.messageStore.messageModule target: root.messageStore.messageModule
function onLinkPreviewDataWasReceived(previewData, uuid) { function onLinkPreviewDataWasReceived(previewData, uuid) {
if (d.uuid !== uuid) if (d.uuid !== uuid)
return return
@ -265,12 +298,9 @@ Flow {
} }
} }
} }
ListModel { ListModel {
id: linksModel id: linksModel
property var rawData property var rawData
onRawDataChanged: { onRawDataChanged: {
linksModel.clear() linksModel.clear()
rawData.links.forEach((link) => { rawData.links.forEach((link) => {
@ -279,35 +309,8 @@ Flow {
} }
} }
Component {
id: unfurledProfileLinkComponent
UserProfileCard {
id: unfurledProfileLink
readonly property var contact: Utils.parseContactUrl(parent.link)
readonly property var contactDetails: Utils.getContactDetailsAsJson(contact.publicKey)
readonly property string nickName: contactDetails ? contactDetails.localNickname : ""
readonly property string ensName: contactDetails ? contactDetails.name : ""
readonly property string displayName: contact && contact.displayName ? contact.displayName :
contactDetails && contactDetails.displayName ? contactDetails.displayName : ""
readonly property string aliasName: contactDetails ? contactDetails.alias : ""
leftTail: !root.isCurrentUser
userName: ProfileUtils.displayName(nickName, ensName, displayName, aliasName)
userPublicKey: contactDetails && contactDetails.publicKey ? contactDetails.publicKey : ""
userBio: contactDetails && contactDetails.bio ? contactDetails.bio : ""
userImage: contactDetails && contactDetails.thumbnailImage ? contactDetails.thumbnailImage : ""
ensVerified: contactDetails && contactDetails.ensVerified ? contactDetails.ensVerified : false
onClicked: {
Global.openProfilePopup(userPublicKey)
}
}
}
Component { Component {
id: enableLinkComponent id: enableLinkComponent
Rectangle { Rectangle {
id: enableLinkRoot id: enableLinkRoot
width: 300 width: 300
@ -316,7 +319,6 @@ Flow {
border.width: 1 border.width: 1
border.color: Style.current.border border.color: Style.current.border
color: Style.current.background color: Style.current.background
StatusFlatRoundButton { StatusFlatRoundButton {
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: Style.current.smallPadding anchors.topMargin: Style.current.smallPadding
@ -327,7 +329,6 @@ Flow {
icon.name: "close-circle" icon.name: "close-circle"
onClicked: linksModel.remove(index) onClicked: linksModel.remove(index)
} }
Image { Image {
id: unfurlingImage id: unfurlingImage
source: Style.png("unfurling-image") source: Style.png("unfurling-image")
@ -337,7 +338,6 @@ Flow {
anchors.top: parent.top anchors.top: parent.top
anchors.topMargin: Style.current.smallPadding anchors.topMargin: Style.current.smallPadding
} }
StatusBaseText { StatusBaseText {
id: enableText id: enableText
text: isImage ? qsTr("Enable automatic image unfurling") : text: isImage ? qsTr("Enable automatic image unfurling") :
@ -349,7 +349,6 @@ Flow {
anchors.topMargin: Style.current.halfPadding anchors.topMargin: Style.current.halfPadding
color: Theme.palette.directColor1 color: Theme.palette.directColor1
} }
StatusBaseText { StatusBaseText {
id: infoText id: infoText
text: qsTr("Once enabled, links posted in the chat may share your metadata with their owners") text: qsTr("Once enabled, links posted in the chat may share your metadata with their owners")
@ -360,13 +359,11 @@ Flow {
font.pixelSize: 13 font.pixelSize: 13
color: Theme.palette.baseColor1 color: Theme.palette.baseColor1
} }
Separator { Separator {
id: sep1 id: sep1
anchors.top: infoText.bottom anchors.top: infoText.bottom
anchors.topMargin: Style.current.smallPadding anchors.topMargin: Style.current.smallPadding
} }
StatusFlatButton { StatusFlatButton {
id: enableBtn id: enableBtn
objectName: "LinksMessageView_enableBtn" objectName: "LinksMessageView_enableBtn"
@ -380,19 +377,16 @@ Flow {
background.radius = 0; background.radius = 0;
} }
} }
Separator { Separator {
id: sep2 id: sep2
anchors.top: enableBtn.bottom anchors.top: enableBtn.bottom
anchors.topMargin: 0 anchors.topMargin: 0
} }
Item { Item {
width: parent.width width: parent.width
height: 44 height: 44
anchors.top: sep2.bottom anchors.top: sep2.bottom
clip: true clip: true
StatusFlatButton { StatusFlatButton {
id: dontAskBtn id: dontAskBtn
width: parent.width width: parent.width

View File

@ -71,7 +71,7 @@ Loader {
return [] return []
const separator = " " const separator = " "
const arr = links.split(separator) const arr = links.split(separator)
const filtered = arr.filter(v => v.toLowerCase().endsWith('.gif') || v.toLowerCase().startsWith(Constants.userLinkPrefix.toLowerCase())) const filtered = arr.filter(v => v.toLowerCase().endsWith('.gif'))
const out = filtered.join(separator) const out = filtered.join(separator)
return out return out
} }

View File

@ -1216,6 +1216,14 @@ QtObject {
} }
enum LinkPreviewType { enum LinkPreviewType {
NoPreview = 0,
Standard = 1,
StatusContact = 2,
StatusCommunity = 3,
StatusCommunityChannel = 4
}
enum StandardLinkPreviewType {
Link = 0, Link = 0,
Image = 1 Image = 1
} }

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit ac813ef5d8f012ba4a0b532483ceeebc553aa3b1 Subproject commit aded258ccb68f88dc995e22f8b4e06157bb642db