feature: support unfurled Status links (contact/community/channel) (#12303)
* chore: move `LinkPreviewThumbnail` to a separate file
This commit is contained in:
parent
9581e6deb6
commit
520d34240a
|
@ -11,6 +11,9 @@ include ../../app_service/service/accounts/utils
|
|||
QtObject:
|
||||
type Utils* = ref object of QObject
|
||||
|
||||
proc isCompressedPubKey*(self: Utils, publicKey: string): bool
|
||||
proc getDecompressedPk*(self: Utils, compressedKey: string): string
|
||||
|
||||
proc setup(self: Utils) =
|
||||
self.QObject.setup
|
||||
|
||||
|
@ -129,13 +132,22 @@ QtObject:
|
|||
result = escape_html(text)
|
||||
|
||||
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.} =
|
||||
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.} =
|
||||
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.} =
|
||||
compressPk(publicKey)
|
||||
|
|
|
@ -192,8 +192,8 @@ proc getLinkPreviewEnabled*(self: Controller): bool =
|
|||
proc canAskToEnableLinkPreview(self: Controller): bool =
|
||||
return self.linkPreviewPersistentSetting == LinkPreviewSetting.AlwaysAsk and self.linkPreviewCurrentMessageSetting == LinkPreviewSetting.AlwaysAsk
|
||||
|
||||
proc setText*(self: Controller, text: string) =
|
||||
if(text == ""):
|
||||
proc setText*(self: Controller, text: string, unfurlNewUrls: bool) =
|
||||
if text == "":
|
||||
self.resetLinkPreviews()
|
||||
return
|
||||
|
||||
|
@ -204,7 +204,10 @@ proc setText*(self: Controller, text: string) =
|
|||
let askToEnableLinkPreview = len(newUrls) > 0 and self.canAskToEnableLinkPreview()
|
||||
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)
|
||||
|
||||
proc linkPreviewsFromCache*(self: Controller, urls: seq[string]): Table[string, LinkPreview] =
|
||||
|
|
|
@ -96,7 +96,7 @@ method isFavorite*(self: AccessInterface, item: GifDto): bool {.base.} =
|
|||
method viewDidLoad*(self: AccessInterface) {.base.} =
|
||||
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")
|
||||
|
||||
method setUrls*(self: AccessInterface, urls: seq[string]) {.base.} =
|
||||
|
|
|
@ -152,8 +152,8 @@ method addToRecentsGif*(self: Module, item: GifDto) =
|
|||
method isFavorite*(self: Module, item: GifDto): bool =
|
||||
return self.controller.isFavorite(item)
|
||||
|
||||
method setText*(self: Module, text: string) =
|
||||
self.controller.setText(text)
|
||||
method setText*(self: Module, text: string, unfurlUrls: bool) =
|
||||
self.controller.setText(text, unfurlUrls)
|
||||
|
||||
method clearLinkPreviewCache*(self: Module) {.slot.} =
|
||||
self.controller.clearLinkPreviewCache()
|
||||
|
|
|
@ -51,9 +51,11 @@ QtObject:
|
|||
msg: string,
|
||||
replyTo: string,
|
||||
contentType: int) {.slot.} =
|
||||
self.delegate.setText(msg, false)
|
||||
self.delegate.sendChatMessage(msg, replyTo, contentType, self.linkPreviewModel.getUnfuledLinkPreviews())
|
||||
|
||||
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())
|
||||
|
||||
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
|
||||
proc setText*(self: View, text: string) {.slot.} =
|
||||
self.delegate.setText(text)
|
||||
self.delegate.setText(text, true)
|
||||
|
||||
proc updateLinkPreviewsFromCache*(self: View, urls: seq[string]) =
|
||||
let linkPreviews = self.delegate.linkPreviewsFromCache(urls)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import strformat
|
||||
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
|
||||
Item* = ref object
|
||||
|
|
|
@ -1,20 +1,37 @@
|
|||
import NimQml, strformat, tables, sequtils
|
||||
import ./link_preview_item
|
||||
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
|
||||
ModelRole {.pure.} = enum
|
||||
Url = UserRole + 1
|
||||
Unfurled
|
||||
Immutable
|
||||
Hostname
|
||||
Title
|
||||
Description
|
||||
LinkType
|
||||
ThumbnailWidth
|
||||
ThumbnailHeight
|
||||
ThumbnailUrl
|
||||
ThumbnailDataUri
|
||||
Empty
|
||||
PreviewType
|
||||
# Standard unfurled link (oembed, opengraph, image)
|
||||
StandardPreview
|
||||
StandardPreviewThumbnail
|
||||
# Status contact
|
||||
StatusContactPreview
|
||||
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:
|
||||
type
|
||||
|
@ -66,14 +83,23 @@ QtObject:
|
|||
ModelRole.Url.int:"url",
|
||||
ModelRole.Unfurled.int:"unfurled",
|
||||
ModelRole.Immutable.int:"immutable",
|
||||
ModelRole.Hostname.int:"hostname",
|
||||
ModelRole.Title.int:"title",
|
||||
ModelRole.Description.int:"description",
|
||||
ModelRole.LinkType.int:"linkType",
|
||||
ModelRole.ThumbnailWidth.int:"thumbnailWidth",
|
||||
ModelRole.ThumbnailHeight.int:"thumbnailHeight",
|
||||
ModelRole.ThumbnailUrl.int:"thumbnailUrl",
|
||||
ModelRole.ThumbnailDataUri.int:"thumbnailDataUri",
|
||||
ModelRole.Empty.int:"empty",
|
||||
ModelRole.PreviewType.int:"previewType",
|
||||
# Standard
|
||||
ModelRole.StandardPreview.int:"standardPreview",
|
||||
ModelRole.StandardPreviewThumbnail.int:"standardPreviewThumbnail",
|
||||
# Contact
|
||||
ModelRole.StatusContactPreview.int:"statusContactPreview",
|
||||
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
|
||||
|
||||
method data(self: Model, index: QModelIndex, role: int): QVariant =
|
||||
|
@ -93,22 +119,45 @@ QtObject:
|
|||
result = newQVariant(item.unfurled)
|
||||
of ModelRole.Immutable:
|
||||
result = newQVariant(item.immutable)
|
||||
of ModelRole.Hostname:
|
||||
result = newQVariant(item.linkPreview.hostname)
|
||||
of ModelRole.Title:
|
||||
result = newQVariant(item.linkPreview.title)
|
||||
of ModelRole.Description:
|
||||
result = newQVariant(item.linkPreview.description)
|
||||
of ModelRole.LinkType:
|
||||
result = newQVariant(item.linkPreview.linkType.int)
|
||||
of ModelRole.ThumbnailWidth:
|
||||
result = newQVariant(item.linkPreview.thumbnail.width)
|
||||
of ModelRole.ThumbnailHeight:
|
||||
result = newQVariant(item.linkPreview.thumbnail.height)
|
||||
of ModelRole.ThumbnailUrl:
|
||||
result = newQVariant(item.linkPreview.thumbnail.url)
|
||||
of ModelRole.ThumbnailDataUri:
|
||||
result = newQVariant(item.linkPreview.thumbnail.dataUri)
|
||||
of ModelRole.Empty:
|
||||
result = newQVariant(item.linkPreview.empty())
|
||||
of ModelRole.PreviewType:
|
||||
result = newQVariant(item.linkPreview.previewType.int)
|
||||
of ModelRole.StandardPreview:
|
||||
if item.linkPreview.standardPreview != nil:
|
||||
result = newQVariant(item.linkPreview.standardPreview)
|
||||
of ModelRole.StandardPreviewThumbnail:
|
||||
if item.linkPreview.standardPreview != nil:
|
||||
result = newQVariant(item.linkPreview.standardPreview.getThumbnail())
|
||||
of ModelRole.StatusContactPreview:
|
||||
if item.linkPreview.statusContactPreview != nil:
|
||||
result = newQVariant(item.linkPreview.statusContactPreview)
|
||||
of ModelRole.StatusContactPreviewThumbnail:
|
||||
if item.linkPreview.statusContactPreview != nil:
|
||||
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) =
|
||||
if(ind < 0 or ind >= self.items.len):
|
||||
|
@ -220,7 +269,7 @@ QtObject:
|
|||
proc getUnfuledLinkPreviews*(self: Model): seq[LinkPreview] =
|
||||
result = @[]
|
||||
for item in self.items:
|
||||
if item.unfurled and item.linkPreview.hostName != "":
|
||||
if item.unfurled and not item.linkPreview.empty():
|
||||
result.add(item.linkPreview)
|
||||
|
||||
proc getLinks*(self: Model): seq[string] =
|
||||
|
|
|
@ -1,85 +1,128 @@
|
|||
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
|
||||
|
||||
type
|
||||
LinkType* {.pure.} = enum
|
||||
Link = 0
|
||||
Image
|
||||
|
||||
proc toLinkType*(value: int): LinkType =
|
||||
try:
|
||||
return LinkType(value)
|
||||
except RangeDefect:
|
||||
return LinkType.Link
|
||||
|
||||
type
|
||||
LinkPreviewThumbnail* = object
|
||||
width*: int
|
||||
height*: int
|
||||
url*: string
|
||||
dataUri*: string
|
||||
PreviewType {.pure.} = enum
|
||||
NoPreview = 0
|
||||
StandardPreview
|
||||
StatusContactPreview
|
||||
StatusCommunityPreview
|
||||
StatusCommunityChannelPreview
|
||||
|
||||
type
|
||||
LinkPreview* = ref object
|
||||
url*: string
|
||||
hostname*: string
|
||||
title*: string
|
||||
description*: string
|
||||
thumbnail*: LinkPreviewThumbnail
|
||||
linkType*: LinkType
|
||||
previewType*: PreviewType
|
||||
standardPreview*: StandardLinkPreview
|
||||
statusContactPreview*: StatusContactLinkPreview
|
||||
statusCommunityPreview*: StatusCommunityLinkPreview
|
||||
statusCommunityChannelPreview*: StatusCommunityChannelLinkPreview
|
||||
|
||||
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 =
|
||||
result = LinkPreview()
|
||||
result.url = url
|
||||
result.previewType = PreviewType.NoPreview
|
||||
|
||||
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 toLinkPreview*(jsonObj: JsonNode): LinkPreview =
|
||||
proc toLinkPreview*(jsonObj: JsonNode, standard: bool): LinkPreview =
|
||||
result = LinkPreview()
|
||||
result.previewType = PreviewType.NoPreview
|
||||
|
||||
if standard:
|
||||
discard jsonObj.getProp("url", result.url)
|
||||
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 jsonObj.getProp("thumbnail", thumbnail):
|
||||
result.thumbnail = toLinkPreviewThumbnail(thumbnail)
|
||||
|
||||
proc `$`*(self: LinkPreviewThumbnail): string =
|
||||
result = fmt"""LinkPreviewThumbnail(
|
||||
width: {self.width},
|
||||
height: {self.height},
|
||||
urlLength: {self.url.len},
|
||||
dataUriLength: {self.dataUri.len}
|
||||
)"""
|
||||
result.previewType = PreviewType.StandardPreview
|
||||
result.standardPreview = toStandardLinkPreview(jsonObj)
|
||||
else:
|
||||
discard jsonObj.getProp("url", result.url)
|
||||
var node: JsonNode
|
||||
if jsonObj.getProp("contact", node):
|
||||
result.previewType = PreviewType.StatusContactPreview
|
||||
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 =
|
||||
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(
|
||||
type: {self.linkType},
|
||||
url: {self.url},
|
||||
hostname: {self.hostname},
|
||||
title: {self.title},
|
||||
description: {self.description},
|
||||
thumbnail: {self.thumbnail}
|
||||
previewType: {self.previewType},
|
||||
standardPreview: {standardPreview},
|
||||
contactPreview: {contactPreview},
|
||||
communityPreview: {communityPreview},
|
||||
channelPreview: {channelPreview}
|
||||
)"""
|
||||
|
||||
# Custom JSON converter to force `linkType` integer instead of string
|
||||
proc `%`*(self: LinkPreview): JsonNode =
|
||||
result = %* {
|
||||
"type": self.linkType.int,
|
||||
"url": self.url,
|
||||
"hostname": self.hostname,
|
||||
"title": self.title,
|
||||
"description": self.description,
|
||||
"thumbnail": %self.thumbnail,
|
||||
"standardPreview": %self.standardPreview,
|
||||
"contactPreview": %self.statusContactPreview,
|
||||
"communityPreview": %self.statusCommunityPreview,
|
||||
"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()
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
{.used.}
|
||||
|
||||
import json, strutils
|
||||
import json, strutils, chronicles
|
||||
import ../../../common/types
|
||||
import link_preview
|
||||
|
||||
|
@ -263,7 +263,12 @@ proc toMessageDto*(jsonObj: JsonNode): MessageDto =
|
|||
var linkPreviewsArr: JsonNode
|
||||
if jsonObj.getProp("linkPreviews", linkPreviewsArr):
|
||||
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
|
||||
if(jsonObj.getProp("parsedText", parsedTextArr) and parsedTextArr.kind == JArray):
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -18,6 +18,7 @@ import ../chat/dto/chat as chat_dto
|
|||
import ./dto/pinned_message_update as pinned_msg_update_dto
|
||||
import ./dto/removed_message as removed_msg_dto
|
||||
import ./dto/link_preview
|
||||
import ./dto/status_link_preview
|
||||
import ./message_cursor
|
||||
|
||||
import ../../common/message as message_common
|
||||
|
@ -863,11 +864,20 @@ QtObject:
|
|||
if responseObj.getProp("requestedUrls", requestedUrlsArr):
|
||||
requestedUrls = map(requestedUrlsArr.getElems(), proc(x: JsonNode): string = x.getStr)
|
||||
|
||||
var linkPreviewsArr: JsonNode
|
||||
let unfurlResponse = responseObj["response"]
|
||||
|
||||
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():
|
||||
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
|
||||
|
||||
for url in requestedUrls:
|
||||
|
|
|
@ -3,6 +3,11 @@ import core, ../app_service/common/utils
|
|||
import response_type
|
||||
import interpret/cropped_image
|
||||
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
|
||||
|
||||
|
@ -66,6 +71,7 @@ proc sendChatMessage*(
|
|||
stickerHash: string = "",
|
||||
stickerPack: string = "0",
|
||||
): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||
let (standardLinkPreviews, statusLinkPreviews) = extractLinkPreviewsLists(linkPreviews)
|
||||
result = callPrivateRPC("sendChatMessage".prefix, %* [
|
||||
{
|
||||
"chatId": chatId,
|
||||
|
@ -78,7 +84,8 @@ proc sendChatMessage*(
|
|||
},
|
||||
"contentType": contentType,
|
||||
"communityId": communityId,
|
||||
"linkPreviews": linkPreviews
|
||||
"linkPreviews": standardLinkPreviews,
|
||||
"statusLinkPreviews": statusLinkPreviews
|
||||
}
|
||||
])
|
||||
|
||||
|
|
|
@ -4,11 +4,121 @@ import QtQuick.Layouts 1.15
|
|||
import shared.views.chat 1.0
|
||||
|
||||
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 {
|
||||
id: messageViewWrapper
|
||||
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
|
||||
|
||||
|
@ -16,57 +126,7 @@ SplitView {
|
|||
|
||||
store: {}
|
||||
messageStore: {}
|
||||
linkPreviewModel: ListModel {
|
||||
id: linkPreviewModel
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
hostname: "www.youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: "PSY - ‘I LUV IT’ M/V @ https://youtu.be/Xvjnoagk6GU PSY - ‘New Face’ M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
|
||||
linkType: 0 // 0 = link, 1 = image
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
}
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
hostname: "www.youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: "PSY - ‘I LUV IT’ M/V @ https://youtu.be/Xvjnoagk6GU PSY - ‘New Face’ M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
|
||||
linkType: 0 // 0 = link, 1 = image
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
}
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
hostname: "www.youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: "PSY - ‘I LUV IT’ M/V @ https://youtu.be/Xvjnoagk6GU PSY - ‘New Face’ M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
|
||||
linkType: 0 // 0 = link, 1 = image
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
}
|
||||
ListElement {
|
||||
url: "https://www.youtube.com/watch?v=9bZkp7q19f0"
|
||||
unfurled: true
|
||||
hostname: "www.youtube.com"
|
||||
title: "PSY - GANGNAM STYLE(강남스타일) M/V"
|
||||
description: "PSY - ‘I LUV IT’ M/V @ https://youtu.be/Xvjnoagk6GU PSY - ‘New Face’ M/V @https://youtu.be/OwJPPaEyqhI PSY - 8TH ALBUM '4X2=8' on iTunes @ https://smarturl.it/PSY_8thAlbum PSY - GANGNAM STYLE(강남스타일) on iTunes @ http://smarturl.it/PsyGangnam #PSY #싸이 #GANGNAMSTYLE #강남스타일 More about PSY@ http://www.psyp..."
|
||||
linkType: 0 // 0 = link, 1 = image
|
||||
thumbnailWidth: 480
|
||||
thumbnailHeight: 360
|
||||
thumbnailUrl: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
thumbnailDataUri: "https://i.ytimg.com/vi/9bZkp7q19f0/hqdefault.jpg"
|
||||
}
|
||||
}
|
||||
linkPreviewModel: mockedLinkPreviewModel
|
||||
localUnfurlLinks: {}
|
||||
isCurrentUser: true
|
||||
|
||||
|
|
|
@ -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}`
|
||||
}
|
||||
}
|
|
@ -101,32 +101,54 @@ Control {
|
|||
model: d.filteredModel
|
||||
delegate: LinkPreviewMiniCard {
|
||||
// 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 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: title
|
||||
domain: hostname //TODO: use domain when available
|
||||
titleStr: standardPreview ? standardPreview.title : statusContactPreview ? statusContactPreview.displayName : ""
|
||||
domain: standardPreview ? standardPreview.hostname : "" //TODO: use domain when available
|
||||
favIconUrl: "" //TODO: use favicon when available
|
||||
communityName: "" //TODO: add community info when available
|
||||
channelName: "" //TODO: add community info when available
|
||||
communityName: statusCommunityPreview ? statusCommunityPreview.displayName : ""
|
||||
channelName: statusCommunityChannelPreview ? statusCommunityChannelPreview.displayName : ""
|
||||
|
||||
thumbnailImageUrl: thumbnailUrl.length > 0 ? thumbnailUrl : thumbnailDataUri
|
||||
type: linkType === 0 ? LinkPreviewMiniCard.Type.Link : LinkPreviewMiniCard.Type.Image
|
||||
previewState: unfurled && hostname != "" ? LinkPreviewMiniCard.State.Loaded :
|
||||
unfurled && hostname === "" ? LinkPreviewMiniCard.State.LoadingFailed :
|
||||
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))
|
||||
|
|
|
@ -21,6 +21,7 @@ CalloutCard {
|
|||
}
|
||||
|
||||
enum Type {
|
||||
Unknown = 0,
|
||||
Link,
|
||||
Image,
|
||||
Community,
|
||||
|
@ -28,6 +29,30 @@ CalloutCard {
|
|||
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 domain
|
||||
required property string communityName
|
||||
|
|
|
@ -18,6 +18,7 @@ InformationTag 1.0 InformationTag.qml
|
|||
InformationTile 1.0 InformationTile.qml
|
||||
Input 1.0 Input.qml
|
||||
LoadingTokenDelegate 1.0 LoadingTokenDelegate.qml
|
||||
LinkPreviewDebugView 1.0 LinkPreviewDebugView.qml
|
||||
RadioButtonSelector 1.0 RadioButtonSelector.qml
|
||||
RecipientSelector 1.0 RecipientSelector.qml
|
||||
SearchBox 1.0 SearchBox.qml
|
||||
|
|
|
@ -7,6 +7,7 @@ import StatusQ.Core.Theme 0.1
|
|||
import StatusQ.Controls 0.1
|
||||
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
|
||||
|
@ -89,33 +90,65 @@ Flow {
|
|||
model: root.linkPreviewModel
|
||||
delegate: Loader {
|
||||
id: linkMessageLoader
|
||||
|
||||
// properties from the model
|
||||
required property string url
|
||||
|
||||
required property bool unfurled
|
||||
required property string hostname
|
||||
required property string title
|
||||
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 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 : ""
|
||||
property bool animated: false
|
||||
|
||||
asynchronous: true
|
||||
active: unfurled && hostname != ""
|
||||
active: unfurled && !empty
|
||||
|
||||
sourceComponent: LinkPreviewCard {
|
||||
id: unfurledLink
|
||||
leftTail: !root.isCurrentUser
|
||||
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 }
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bannerImageSource: thumbnailUrl.length > 0 ? thumbnailUrl : thumbnailDataUri
|
||||
title: parent.title
|
||||
description: parent.description
|
||||
footer: hostname
|
||||
onClicked:
|
||||
(mouse) => {
|
||||
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
|
||||
|
@ -125,8 +158,22 @@ Flow {
|
|||
break
|
||||
}
|
||||
}
|
||||
highlight: root.highlightLink === url
|
||||
onHoveredChanged: linksRepeater.hoveredUrl = hovered ? url : ""
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
Component {
|
||||
id: unfurledImageComponent
|
||||
|
||||
CalloutCard {
|
||||
implicitWidth: linkImage.width
|
||||
implicitHeight: linkImage.height
|
||||
leftTail: !root.isCurrentUser
|
||||
|
||||
StatusChatImageLoader {
|
||||
id: linkImage
|
||||
|
||||
readonly property bool globalAnimationEnabled: root.messageStore.playAnimation
|
||||
readonly property string urlLink: link
|
||||
property bool localAnimationEnabled: true
|
||||
|
@ -195,15 +239,11 @@ Flow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Code below can be dropped when New unfurling flow suppports GIFs.
|
||||
|
||||
Component {
|
||||
id: invitationBubble
|
||||
|
||||
InvitationBubbleView {
|
||||
property var invitationData: root.store.getLinkDataForStatusLinks(link)
|
||||
|
||||
store: root.store
|
||||
communityId: invitationData && invitationData.communityData ? invitationData.communityData.communityId : ""
|
||||
communityData: invitationData && invitationData.communityData ? invitationData.communityData : null
|
||||
|
@ -214,7 +254,6 @@ Flow {
|
|||
if (!invitationData)
|
||||
linksModel.remove(index)
|
||||
}
|
||||
|
||||
Connections {
|
||||
enabled: !!invitationData && invitationData.fetching
|
||||
target: root.store.communitiesModuleInst
|
||||
|
@ -225,10 +264,8 @@ Flow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: d
|
||||
|
||||
readonly property string uuid: Utils.uuid()
|
||||
readonly property string whiteListedImgExtensions: Constants.acceptedImageExtensions.toString()
|
||||
readonly property string whiteListedUrls: JSON.stringify(localAccountSensitiveSettings.whitelistedUnfurlingSites)
|
||||
|
@ -241,18 +278,14 @@ Flow {
|
|||
whiteListedImgExtensions,
|
||||
localAccountSensitiveSettings.displayChatImages)
|
||||
}
|
||||
|
||||
|
||||
onGetLinkPreviewDataIdChanged: {
|
||||
linkFetchConnections.enabled = root.localUnfurlLinks !== ""
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
id: linkFetchConnections
|
||||
enabled: false
|
||||
target: root.messageStore.messageModule
|
||||
|
||||
function onLinkPreviewDataWasReceived(previewData, uuid) {
|
||||
if (d.uuid !== uuid)
|
||||
return
|
||||
|
@ -265,12 +298,9 @@ Flow {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: linksModel
|
||||
|
||||
property var rawData
|
||||
|
||||
onRawDataChanged: {
|
||||
linksModel.clear()
|
||||
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 {
|
||||
id: enableLinkComponent
|
||||
|
||||
Rectangle {
|
||||
id: enableLinkRoot
|
||||
width: 300
|
||||
|
@ -316,7 +319,6 @@ Flow {
|
|||
border.width: 1
|
||||
border.color: Style.current.border
|
||||
color: Style.current.background
|
||||
|
||||
StatusFlatRoundButton {
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
|
@ -327,7 +329,6 @@ Flow {
|
|||
icon.name: "close-circle"
|
||||
onClicked: linksModel.remove(index)
|
||||
}
|
||||
|
||||
Image {
|
||||
id: unfurlingImage
|
||||
source: Style.png("unfurling-image")
|
||||
|
@ -337,7 +338,6 @@ Flow {
|
|||
anchors.top: parent.top
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: enableText
|
||||
text: isImage ? qsTr("Enable automatic image unfurling") :
|
||||
|
@ -349,7 +349,6 @@ Flow {
|
|||
anchors.topMargin: Style.current.halfPadding
|
||||
color: Theme.palette.directColor1
|
||||
}
|
||||
|
||||
StatusBaseText {
|
||||
id: infoText
|
||||
text: qsTr("Once enabled, links posted in the chat may share your metadata with their owners")
|
||||
|
@ -360,13 +359,11 @@ Flow {
|
|||
font.pixelSize: 13
|
||||
color: Theme.palette.baseColor1
|
||||
}
|
||||
|
||||
Separator {
|
||||
id: sep1
|
||||
anchors.top: infoText.bottom
|
||||
anchors.topMargin: Style.current.smallPadding
|
||||
}
|
||||
|
||||
StatusFlatButton {
|
||||
id: enableBtn
|
||||
objectName: "LinksMessageView_enableBtn"
|
||||
|
@ -380,19 +377,16 @@ Flow {
|
|||
background.radius = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Separator {
|
||||
id: sep2
|
||||
anchors.top: enableBtn.bottom
|
||||
anchors.topMargin: 0
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: 44
|
||||
anchors.top: sep2.bottom
|
||||
clip: true
|
||||
|
||||
StatusFlatButton {
|
||||
id: dontAskBtn
|
||||
width: parent.width
|
||||
|
|
|
@ -71,7 +71,7 @@ Loader {
|
|||
return []
|
||||
const 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)
|
||||
return out
|
||||
}
|
||||
|
|
|
@ -1216,6 +1216,14 @@ QtObject {
|
|||
}
|
||||
|
||||
enum LinkPreviewType {
|
||||
NoPreview = 0,
|
||||
Standard = 1,
|
||||
StatusContact = 2,
|
||||
StatusCommunity = 3,
|
||||
StatusCommunityChannel = 4
|
||||
}
|
||||
|
||||
enum StandardLinkPreviewType {
|
||||
Link = 0,
|
||||
Image = 1
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit ac813ef5d8f012ba4a0b532483ceeebc553aa3b1
|
||||
Subproject commit aded258ccb68f88dc995e22f8b4e06157bb642db
|
Loading…
Reference in New Issue