diff --git a/src/app/global/utils.nim b/src/app/global/utils.nim index 1f808cd671..fd9f4179f8 100644 --- a/src/app/global/utils.nim +++ b/src/app/global/utils.nim @@ -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) diff --git a/src/app/modules/main/chat_section/chat_content/input_area/controller.nim b/src/app/modules/main/chat_section/chat_content/input_area/controller.nim index 1505bbb480..73d2d809fe 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/controller.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/controller.nim @@ -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] = @@ -232,4 +235,4 @@ proc setLinkPreviewEnabled*(self: Controller, enabled: bool) = self.linkPreviewPersistentSetting = LinkPreviewSetting.Disabled self.linkPreviewCurrentMessageSetting = LinkPreviewSetting.Disabled - self.delegate.setAskToEnableLinkPreview(false) \ No newline at end of file + self.delegate.setAskToEnableLinkPreview(false) diff --git a/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim b/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim index 1408c9f2bf..ccc0b8c04d 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/io_interface.nim @@ -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.} = diff --git a/src/app/modules/main/chat_section/chat_content/input_area/module.nim b/src/app/modules/main/chat_section/chat_content/input_area/module.nim index 6672af604f..b0f1ecd620 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/module.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/module.nim @@ -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() diff --git a/src/app/modules/main/chat_section/chat_content/input_area/view.nim b/src/app/modules/main/chat_section/chat_content/input_area/view.nim index 167efda3c0..71ae996bd8 100644 --- a/src/app/modules/main/chat_section/chat_content/input_area/view.nim +++ b/src/app/modules/main/chat_section/chat_content/input_area/view.nim @@ -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) @@ -252,4 +254,4 @@ QtObject: self.loadLinkPreviews(links) proc removeLinkPreviewData*(self: View, index: int) {.slot.} = - self.linkPreviewModel.removePreviewData(index) \ No newline at end of file + self.linkPreviewModel.removePreviewData(index) diff --git a/src/app/modules/shared_models/link_preview_item.nim b/src/app/modules/shared_models/link_preview_item.nim index d514c3643b..8488f44fb7 100644 --- a/src/app/modules/shared_models/link_preview_item.nim +++ b/src/app/modules/shared_models/link_preview_item.nim @@ -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 diff --git a/src/app/modules/shared_models/link_preview_model.nim b/src/app/modules/shared_models/link_preview_model.nim index 691b0ddf56..bf23770b5c 100644 --- a/src/app/modules/shared_models/link_preview_model.nim +++ b/src/app/modules/shared_models/link_preview_model.nim @@ -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,10 +269,10 @@ 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] = result = @[] for item in self.items: - result.add(item.linkPreview.url) \ No newline at end of file + result.add(item.linkPreview.url) diff --git a/src/app_service/service/message/dto/link_preview.nim b/src/app_service/service/message/dto/link_preview.nim index 41026239cd..ccd0a66fb4 100644 --- a/src/app_service/service/message/dto/link_preview.nim +++ b/src/app_service/service/message/dto/link_preview.nim @@ -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() - 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) + result.previewType = PreviewType.NoPreview - 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} - )""" + if standard: + discard jsonObj.getProp("url", result.url) + 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() diff --git a/src/app_service/service/message/dto/link_preview_thumbnail.nim b/src/app_service/service/message/dto/link_preview_thumbnail.nim new file mode 100644 index 0000000000..c6e7e9b649 --- /dev/null +++ b/src/app_service/service/message/dto/link_preview_thumbnail.nim @@ -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 + } diff --git a/src/app_service/service/message/dto/message.nim b/src/app_service/service/message/dto/message.nim index 2dccaaec69..8cf620b12b 100644 --- a/src/app_service/service/message/dto/message.nim +++ b/src/app_service/service/message/dto/message.nim @@ -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): diff --git a/src/app_service/service/message/dto/standard_link_preview.nim b/src/app_service/service/message/dto/standard_link_preview.nim new file mode 100644 index 0000000000..da8fb8c44c --- /dev/null +++ b/src/app_service/service/message/dto/standard_link_preview.nim @@ -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 diff --git a/src/app_service/service/message/dto/status_community_channel_link_preview.nim b/src/app_service/service/message/dto/status_community_channel_link_preview.nim new file mode 100644 index 0000000000..d54c2108c6 --- /dev/null +++ b/src/app_service/service/message/dto/status_community_channel_link_preview.nim @@ -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 diff --git a/src/app_service/service/message/dto/status_community_link_preview.nim b/src/app_service/service/message/dto/status_community_link_preview.nim new file mode 100644 index 0000000000..90d8f0195c --- /dev/null +++ b/src/app_service/service/message/dto/status_community_link_preview.nim @@ -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 diff --git a/src/app_service/service/message/dto/status_contact_link_preview.nim b/src/app_service/service/message/dto/status_contact_link_preview.nim new file mode 100644 index 0000000000..4c6d6119f3 --- /dev/null +++ b/src/app_service/service/message/dto/status_contact_link_preview.nim @@ -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 diff --git a/src/app_service/service/message/dto/status_link_preview.nim b/src/app_service/service/message/dto/status_link_preview.nim new file mode 100644 index 0000000000..922a29887f --- /dev/null +++ b/src/app_service/service/message/dto/status_link_preview.nim @@ -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 diff --git a/src/app_service/service/message/service.nim b/src/app_service/service/message/service.nim index 809da11056..ba2f9f9391 100644 --- a/src/app_service/service/message/service.nim +++ b/src/app_service/service/message/service.nim @@ -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: diff --git a/src/backend/chat.nim b/src/backend/chat.nim index febc895ac8..4eca7b0e48 100644 --- a/src/backend/chat.nim +++ b/src/backend/chat.nim @@ -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 } ]) diff --git a/storybook/pages/LinksMessageViewPage.qml b/storybook/pages/LinksMessageViewPage.qml index 20b6c512d6..55720bacbf 100644 --- a/storybook/pages/LinksMessageViewPage.qml +++ b/storybook/pages/LinksMessageViewPage.qml @@ -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 @@ -94,4 +154,4 @@ SplitView { } } } -} \ No newline at end of file +} diff --git a/ui/imports/shared/controls/LinkPreviewDebugView.qml b/ui/imports/shared/controls/LinkPreviewDebugView.qml new file mode 100644 index 0000000000..67e9b17678 --- /dev/null +++ b/ui/imports/shared/controls/LinkPreviewDebugView.qml @@ -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}` + } +} diff --git a/ui/imports/shared/controls/chat/ChatInputLinksPreviewArea.qml b/ui/imports/shared/controls/chat/ChatInputLinksPreviewArea.qml index 2b1c0fcdd0..5eaee9773f 100644 --- a/ui/imports/shared/controls/chat/ChatInputLinksPreviewArea.qml +++ b/ui/imports/shared/controls/chat/ChatInputLinksPreviewArea.qml @@ -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)) diff --git a/ui/imports/shared/controls/chat/LinkPreviewMiniCard.qml b/ui/imports/shared/controls/chat/LinkPreviewMiniCard.qml index a956d467dc..f8a8bcbc58 100644 --- a/ui/imports/shared/controls/chat/LinkPreviewMiniCard.qml +++ b/ui/imports/shared/controls/chat/LinkPreviewMiniCard.qml @@ -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 diff --git a/ui/imports/shared/controls/qmldir b/ui/imports/shared/controls/qmldir index 76865aa1d0..87b53268dc 100644 --- a/ui/imports/shared/controls/qmldir +++ b/ui/imports/shared/controls/qmldir @@ -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 diff --git a/ui/imports/shared/views/chat/LinksMessageView.qml b/ui/imports/shared/views/chat/LinksMessageView.qml index 9300c4efb2..9fa9f94c84 100644 --- a/ui/imports/shared/views/chat/LinksMessageView.qml +++ b/ui/imports/shared/views/chat/LinksMessageView.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,44 +90,90 @@ 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 - - bannerImageSource: thumbnailUrl.length > 0 ? thumbnailUrl : thumbnailDataUri - title: parent.title - description: parent.description - footer: 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 - } + 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 } } - 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 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 diff --git a/ui/imports/shared/views/chat/MessageView.qml b/ui/imports/shared/views/chat/MessageView.qml index 3ce22ef5ab..069c08ae2c 100644 --- a/ui/imports/shared/views/chat/MessageView.qml +++ b/ui/imports/shared/views/chat/MessageView.qml @@ -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 } diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index 373d634b56..2575617b37 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -1216,6 +1216,14 @@ QtObject { } enum LinkPreviewType { + NoPreview = 0, + Standard = 1, + StatusContact = 2, + StatusCommunity = 3, + StatusCommunityChannel = 4 + } + + enum StandardLinkPreviewType { Link = 0, Image = 1 } diff --git a/vendor/status-go b/vendor/status-go index ac813ef5d8..aded258ccb 160000 --- a/vendor/status-go +++ b/vendor/status-go @@ -1 +1 @@ -Subproject commit ac813ef5d8f012ba4a0b532483ceeebc553aa3b1 +Subproject commit aded258ccb68f88dc995e22f8b4e06157bb642db