From 444072f599750e25d3a2caaf96291a35b5fcb06a Mon Sep 17 00:00:00 2001 From: Jonathan Rainville Date: Fri, 15 Jan 2021 15:33:32 -0500 Subject: [PATCH] feat: call getPreviewData (url unfurling) in a separate thread Fixes #1678 --- src/app/chat/view.nim | 17 +++++-- src/status/chat.nim | 3 -- src/status/libstatus/chat.nim | 6 ++- .../MessageComponents/LinksMessage.qml | 49 ++++++++++++++----- 4 files changed, 53 insertions(+), 22 deletions(-) diff --git a/src/app/chat/view.nim b/src/app/chat/view.nim index fae172a927..8fb8c6e3e2 100644 --- a/src/app/chat/view.nim +++ b/src/app/chat/view.nim @@ -1,6 +1,7 @@ import NimQml, Tables, json, sequtils, chronicles, times, re, sugar, strutils, os, strformat import ../../status/status import ../../status/mailservers +import ../../status/libstatus/chat as libstatus_chat import ../../status/libstatus/accounts/constants import ../../status/libstatus/mailservers as status_mailservers import ../../status/libstatus/types @@ -386,11 +387,17 @@ QtObject: proc copyToClipboard*(self: ChatsView, content: string) {.slot.} = setClipBoardText(content) - proc getLinkPreviewData*(self: ChatsView, link: string): string {.slot.} = - try: - $self.status.chat.getLinkPreviewData(link) - except RpcException as e: - $ %* { "error": e.msg } + proc linkPreviewDataWasReceived*(self: ChatsView, previewData: string) {.signal.} + + proc linkPreviewDataReceived(self: ChatsView, previewData: string) {.slot.} = + self.linkPreviewDataWasReceived(previewData) + + proc getLinkPreviewData*(self: ChatsView, link: string, uuid: string) {.slot.} = + spawnAndSend(self, "linkPreviewDataReceived") do: + var success: bool + # We need to call directly on libstatus because going through the status model is not thread safe + let response = libstatus_chat.getLinkPreviewData(link, success) + $(%* { "result": %response, "success": %success, "uuid": %uuid }) proc joinChat*(self: ChatsView, channel: string, chatTypeInt: int): int {.slot.} = self.status.chat.join(channel, ChatType(chatTypeInt)) diff --git a/src/status/chat.nim b/src/status/chat.nim index a86da08dc2..89fb05d085 100644 --- a/src/status/chat.nim +++ b/src/status/chat.nim @@ -218,9 +218,6 @@ proc clearHistory*(self: ChatModel, chatId: string) = let chat = self.channels[chatId] self.events.emit("chatHistoryCleared", ChannelArgs(chat: chat)) -proc getLinkPreviewData*(self: ChatModel, link: string): JsonNode = - result = status_chat.getLinkPreviewData(link) - proc setActiveChannel*(self: ChatModel, chatId: string) = self.events.emit("activeChannelChanged", ChatIdArg(chatId: chatId)) diff --git a/src/status/libstatus/chat.nim b/src/status/libstatus/chat.nim index 8fb28ce077..ca246e7df5 100644 --- a/src/status/libstatus/chat.nim +++ b/src/status/libstatus/chat.nim @@ -201,14 +201,16 @@ proc muteChat*(chatId: string): string = proc unmuteChat*(chatId: string): string = result = callPrivateRPC("unmuteChat".prefix, %*[chatId]) -proc getLinkPreviewData*(link: string): JsonNode = +proc getLinkPreviewData*(link: string, success: var bool): JsonNode = let responseStr = callPrivateRPC("getLinkPreviewData".prefix, %*[link]) response = Json.decode(responseStr, RpcResponseTyped[JsonNode], allowUnknownFields = false) if not response.error.isNil: - raise newException(RpcException, fmt"""Error getting link preview data for '{link}': {response.error.message}""") + success = false + return %* { "error": fmt"""Error getting link preview data for '{link}': {response.error.message}""" } + success = true response.result proc getAllComunities*(): seq[Community] = diff --git a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/LinksMessage.qml b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/LinksMessage.qml index f1e6b2a147..dc680d64e2 100644 --- a/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/LinksMessage.qml +++ b/ui/app/AppLayouts/Chat/ChatColumn/MessageComponents/LinksMessage.qml @@ -12,6 +12,7 @@ Column { property string linkUrls: "" property var container property bool isCurrentUser: false + readonly property string uuid: Utils.uuid() spacing: Style.current.halfPadding ListModel { @@ -32,6 +33,7 @@ Column { delegate: Loader { id: linkMessageLoader + property bool fetched: false property var linkData property int linkWidth: linksRepeater.width active: true @@ -51,6 +53,37 @@ Column { linkMessageLoader.sourceComponent = linkMessageLoader.getSourceComponent() } } + Connections { + target: chatsModel + onLinkPreviewDataWasReceived: { + let response + try { + response = JSON.parse(previewData) + + } catch (e) { + console.error(previewData, e) + return + } + + + if (response.uuid !== root.uuid) return + + if (!response.success) { + console.error(response.result.error) + return undefined + } + + linkData = response.result + + if (linkData.contentType.startsWith("image/")) { + return linkMessageLoader.sourceComponent = unfurledImageComponent + } + if (linkData.site && linkData.title) { + linkData.address = link + return linkMessageLoader.sourceComponent = unfurledLinkComponent + } + } + } function getSourceComponent() { // Reset the height in case we set it to 0 below. See note below @@ -75,19 +108,11 @@ Column { return enableLinkComponent } if (linkWhiteListed) { - const data = chatsModel.getLinkPreviewData(link) - linkData = JSON.parse(data) - if (linkData.error) { - console.error(linkData.error) - return undefined - } - if (linkData.contentType.startsWith("image/")) { - return unfurledImageComponent - } - if (linkData.site && linkData.title) { - linkData.address = link - return unfurledLinkComponent + if (fetched) { + return } + fetched = true + return chatsModel.getLinkPreviewData(link, root.uuid) } // setting the height to 0 allows the "enable link" dialog to // disappear correctly when appSettings.neverAskAboutUnfurlingAgain