Fix/issue 12651 unfurl status links (#12751)
This commit is contained in:
parent
03d4fbcc48
commit
4239f77941
|
@ -1,4 +1,4 @@
|
||||||
import io_interface, chronicles, tables, sequtils, strutils, sugar
|
import io_interface, tables, sequtils, strutils, sugar, sets
|
||||||
|
|
||||||
|
|
||||||
import ../../../../../../app_service/service/settings/service as settings_service
|
import ../../../../../../app_service/service/settings/service as settings_service
|
||||||
|
@ -8,6 +8,7 @@ import ../../../../../../app_service/service/chat/service as chat_service
|
||||||
import ../../../../../../app_service/service/gif/service as gif_service
|
import ../../../../../../app_service/service/gif/service as gif_service
|
||||||
import ../../../../../../app_service/service/gif/dto
|
import ../../../../../../app_service/service/gif/dto
|
||||||
import ../../../../../../app_service/service/message/dto/link_preview
|
import ../../../../../../app_service/service/message/dto/link_preview
|
||||||
|
import ../../../../../../app_service/service/message/dto/urls_unfurling_plan
|
||||||
import ../../../../../../app_service/service/settings/dto/settings
|
import ../../../../../../app_service/service/settings/dto/settings
|
||||||
import ../../../../../core/eventemitter
|
import ../../../../../core/eventemitter
|
||||||
import ../../../../../core/unique_event_emitter
|
import ../../../../../core/unique_event_emitter
|
||||||
|
@ -28,6 +29,8 @@ type
|
||||||
linkPreviewCache: LinkPreviewCache
|
linkPreviewCache: LinkPreviewCache
|
||||||
linkPreviewPersistentSetting: UrlUnfurlingMode
|
linkPreviewPersistentSetting: UrlUnfurlingMode
|
||||||
linkPreviewCurrentMessageSetting: UrlUnfurlingMode
|
linkPreviewCurrentMessageSetting: UrlUnfurlingMode
|
||||||
|
unfurlRequests: HashSet[string]
|
||||||
|
unfurlingPlan: UrlsUnfurlingPlan
|
||||||
|
|
||||||
proc newController*(
|
proc newController*(
|
||||||
delegate: io_interface.AccessInterface,
|
delegate: io_interface.AccessInterface,
|
||||||
|
@ -55,10 +58,15 @@ proc newController*(
|
||||||
result.linkPreviewCache = newLinkPreiewCache()
|
result.linkPreviewCache = newLinkPreiewCache()
|
||||||
result.linkPreviewPersistentSetting = settingsService.urlUnfurlingMode()
|
result.linkPreviewPersistentSetting = settingsService.urlUnfurlingMode()
|
||||||
result.linkPreviewCurrentMessageSetting = result.linkPreviewPersistentSetting
|
result.linkPreviewCurrentMessageSetting = result.linkPreviewPersistentSetting
|
||||||
|
result.unfurlRequests = initHashSet[string]()
|
||||||
|
result.unfurlingPlan = initUrlsUnfurlingPlan()
|
||||||
|
|
||||||
proc onUnfurlingModeChanged(self: Controller, value: UrlUnfurlingMode)
|
proc onUnfurlingModeChanged(self: Controller, value: UrlUnfurlingMode)
|
||||||
proc onUrlsUnfurled(self: Controller, args: LinkPreviewV2DataArgs)
|
proc onUrlsUnfurled(self: Controller, args: LinkPreviewDataArgs)
|
||||||
proc clearLinkPreviewCache*(self: Controller)
|
proc clearLinkPreviewCache*(self: Controller)
|
||||||
|
proc asyncUnfurlUrls(self: Controller, urls: seq[string])
|
||||||
|
proc asyncUnfurlUnknownUrls(self: Controller, urls: seq[string])
|
||||||
|
proc handleUnfurlingPlan*(self: Controller, unfurlNewUrls: bool)
|
||||||
|
|
||||||
proc delete*(self: Controller) =
|
proc delete*(self: Controller) =
|
||||||
self.events.disconnect()
|
self.events.disconnect()
|
||||||
|
@ -93,7 +101,10 @@ proc init*(self: Controller) =
|
||||||
self.delegate.searchGifsError()
|
self.delegate.searchGifsError()
|
||||||
|
|
||||||
self.events.on(SIGNAL_URLS_UNFURLED) do(e:Args):
|
self.events.on(SIGNAL_URLS_UNFURLED) do(e:Args):
|
||||||
let args = LinkPreviewV2DataArgs(e)
|
let args = LinkPreviewDataArgs(e)
|
||||||
|
if not self.unfurlRequests.contains(args.requestUuid):
|
||||||
|
return
|
||||||
|
self.unfurlRequests.excl(args.requestUuid)
|
||||||
self.onUrlsUnfurled(args)
|
self.onUrlsUnfurled(args)
|
||||||
|
|
||||||
self.events.on(SIGNAL_URL_UNFURLING_MODE_UPDATED) do(e:Args):
|
self.events.on(SIGNAL_URL_UNFURLING_MODE_UPDATED) do(e:Args):
|
||||||
|
@ -195,7 +206,7 @@ proc isFavorite*(self: Controller, item: GifDto): bool =
|
||||||
proc getLinkPreviewEnabled*(self: Controller): bool =
|
proc getLinkPreviewEnabled*(self: Controller): bool =
|
||||||
return self.linkPreviewPersistentSetting == UrlUnfurlingMode.Enabled or self.linkPreviewCurrentMessageSetting == UrlUnfurlingMode.Enabled
|
return self.linkPreviewPersistentSetting == UrlUnfurlingMode.Enabled or self.linkPreviewCurrentMessageSetting == UrlUnfurlingMode.Enabled
|
||||||
|
|
||||||
proc canAskToEnableLinkPreview(self: Controller): bool =
|
proc shouldAskToEnableLinkPreview(self: Controller): bool =
|
||||||
return self.linkPreviewPersistentSetting == UrlUnfurlingMode.AlwaysAsk and self.linkPreviewCurrentMessageSetting == UrlUnfurlingMode.AlwaysAsk
|
return self.linkPreviewPersistentSetting == UrlUnfurlingMode.AlwaysAsk and self.linkPreviewCurrentMessageSetting == UrlUnfurlingMode.AlwaysAsk
|
||||||
|
|
||||||
proc setText*(self: Controller, text: string, unfurlNewUrls: bool) =
|
proc setText*(self: Controller, text: string, unfurlNewUrls: bool) =
|
||||||
|
@ -204,22 +215,64 @@ proc setText*(self: Controller, text: string, unfurlNewUrls: bool) =
|
||||||
self.delegate.setUrls(@[])
|
self.delegate.setUrls(@[])
|
||||||
return
|
return
|
||||||
|
|
||||||
let urls = self.messageService.getTextUrls(text)
|
self.unfurlingPlan = self.messageService.getTextURLsToUnfurl(text)
|
||||||
self.delegate.setUrls(urls)
|
self.handleUnfurlingPlan(unfurlNewUrls)
|
||||||
|
|
||||||
let supportedUrls = urls.filter(x => not x.endsWith(".gif")) # GIFs are currently unfurled by receiver
|
proc handleUnfurlingPlan*(self: Controller, unfurlNewUrls: bool) =
|
||||||
self.delegate.setLinkPreviewUrls(supportedUrls)
|
var allUrls = newSeq[string]() # Used for URLs syntax highlighting only
|
||||||
let newUrls = self.linkPreviewCache.unknownUrls(supportedUrls)
|
var allAllowedUrls = newSeq[string]() # Used for LinkPreviewsModel to keep the urls order
|
||||||
|
var statusAllowedUrls = newSeq[string]()
|
||||||
|
var otherAllowedUrls = newSeq[string]()
|
||||||
|
var askToEnableLinkPreview = false
|
||||||
|
|
||||||
let askToEnableLinkPreview = len(newUrls) > 0 and self.canAskToEnableLinkPreview()
|
for metadata in self.unfurlingPlan.urls:
|
||||||
|
allUrls.add(metadata.url)
|
||||||
|
|
||||||
|
if metadata.permission == UrlUnfurlingForbiddenBySettings or
|
||||||
|
metadata.permission == UrlUnfurlingNotSupported:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if metadata.permission == UrlUnfurlingAskUser:
|
||||||
|
if self.linkPreviewCurrentMessageSetting == UrlUnfurlingMode.AlwaysAsk:
|
||||||
|
askToEnableLinkPreview = true
|
||||||
|
else:
|
||||||
|
otherAllowedUrls.add(metadata.url)
|
||||||
|
allAllowedUrls.add(metadata.url)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Split unfurling into 2 packs, which will be different RPCs.
|
||||||
|
# In most cases we expect status links to ufurl immediately.
|
||||||
|
# In future we could unfurl each link in a separate RPC,
|
||||||
|
# this would give better UX, but might result in worse performance.
|
||||||
|
if metadata.isStatusSharedUrl:
|
||||||
|
statusAllowedUrls.add(metadata.url)
|
||||||
|
else:
|
||||||
|
otherAllowedUrls.add(metadata.url)
|
||||||
|
|
||||||
|
allAllowedUrls.add(metadata.url)
|
||||||
|
|
||||||
|
# Update UI
|
||||||
|
self.delegate.setUrls(allUrls)
|
||||||
|
self.delegate.setLinkPreviewUrls(allAllowedUrls)
|
||||||
self.delegate.setAskToEnableLinkPreview(askToEnableLinkPreview)
|
self.delegate.setAskToEnableLinkPreview(askToEnableLinkPreview)
|
||||||
|
|
||||||
if not unfurlNewUrls:
|
if not unfurlNewUrls:
|
||||||
return
|
return
|
||||||
|
|
||||||
if self.getLinkPreviewEnabled() and len(newUrls) > 0:
|
self.asyncUnfurlUnknownUrls(statusAllowedUrls)
|
||||||
self.messageService.asyncUnfurlUrls(newUrls)
|
self.asyncUnfurlUnknownUrls(otherAllowedUrls)
|
||||||
self.linkPreviewCache.markAsRequested(newUrls)
|
|
||||||
|
proc reloadUnfurlingPlan*(self: Controller) =
|
||||||
|
self.setText(self.delegate.getPlainText(), true)
|
||||||
|
|
||||||
|
proc asyncUnfurlUrls(self: Controller, urls: seq[string]) =
|
||||||
|
let requestUuid = self.messageService.asyncUnfurlUrls(urls)
|
||||||
|
self.unfurlRequests.incl(requestUuid)
|
||||||
|
self.linkPreviewCache.markAsRequested(urls)
|
||||||
|
|
||||||
|
proc asyncUnfurlUnknownUrls(self: Controller, urls: seq[string]) =
|
||||||
|
let newUrls = self.linkPreviewCache.unknownUrls(urls)
|
||||||
|
self.asyncUnfurlUrls(newUrls)
|
||||||
|
|
||||||
proc linkPreviewsFromCache*(self: Controller, urls: seq[string]): Table[string, LinkPreview] =
|
proc linkPreviewsFromCache*(self: Controller, urls: seq[string]): Table[string, LinkPreview] =
|
||||||
return self.linkPreviewCache.linkPreviews(urls)
|
return self.linkPreviewCache.linkPreviews(urls)
|
||||||
|
@ -227,24 +280,18 @@ proc linkPreviewsFromCache*(self: Controller, urls: seq[string]): Table[string,
|
||||||
proc clearLinkPreviewCache*(self: Controller) =
|
proc clearLinkPreviewCache*(self: Controller) =
|
||||||
self.linkPreviewCache.clear()
|
self.linkPreviewCache.clear()
|
||||||
|
|
||||||
proc onUrlsUnfurled(self: Controller, args: LinkPreviewV2DataArgs) =
|
proc onUrlsUnfurled(self: Controller, args: LinkPreviewDataArgs) =
|
||||||
if not self.getLinkPreviewEnabled():
|
|
||||||
return
|
|
||||||
|
|
||||||
let urls = self.linkPreviewCache.add(args.linkPreviews)
|
let urls = self.linkPreviewCache.add(args.linkPreviews)
|
||||||
self.delegate.updateLinkPreviewsFromCache(urls)
|
self.delegate.updateLinkPreviewsFromCache(urls)
|
||||||
|
|
||||||
proc loadLinkPreviews*(self: Controller, urls: seq[string]) =
|
proc loadLinkPreviews*(self: Controller, urls: seq[string]) =
|
||||||
if self.getLinkPreviewEnabled():
|
if self.getLinkPreviewEnabled():
|
||||||
self.messageService.asyncUnfurlUrls(urls)
|
self.asyncUnfurlUrls(urls)
|
||||||
|
|
||||||
proc setLinkPreviewEnabled*(self: Controller, enabled: bool) =
|
proc setLinkPreviewEnabled*(self: Controller, enabled: bool) =
|
||||||
if enabled:
|
let mode = if enabled: UrlUnfurlingMode.Enabled else: UrlUnfurlingMode.Disabled
|
||||||
discard self.settingsService.saveUrlUnfurlingMode(UrlUnfurlingMode.Enabled)
|
discard self.settingsService.saveUrlUnfurlingMode(mode)
|
||||||
return
|
|
||||||
discard self.settingsService.saveUrlUnfurlingMode(UrlUnfurlingMode.Disabled)
|
|
||||||
|
|
||||||
proc onUnfurlingModeChanged(self: Controller, value: UrlUnfurlingMode) =
|
proc onUnfurlingModeChanged(self: Controller, value: UrlUnfurlingMode) =
|
||||||
self.linkPreviewPersistentSetting = value
|
self.linkPreviewPersistentSetting = value
|
||||||
self.resetLinkPreviews()
|
self.reloadUnfurlingPlan()
|
||||||
self.setText(self.delegate.getPlainText(), self.getLinkPreviewEnabled())
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import NimQml, tables
|
import NimQml, tables, sets
|
||||||
|
|
||||||
import ../../../../../../app_service/service/gif/dto
|
import ../../../../../../app_service/service/gif/dto
|
||||||
import ../../../../../../app_service/service/message/dto/link_preview
|
import ../../../../../../app_service/service/message/dto/link_preview
|
||||||
|
@ -114,6 +114,9 @@ method clearLinkPreviewCache*(self: AccessInterface) {.base.} =
|
||||||
method linkPreviewsFromCache*(self: AccessInterface, urls: seq[string]): Table[string, LinkPreview] {.base.} =
|
method linkPreviewsFromCache*(self: AccessInterface, urls: seq[string]): Table[string, LinkPreview] {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
method reloadUnfurlingPlan*(self: AccessInterface) =
|
||||||
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
method loadLinkPreviews*(self: AccessInterface, urls: seq[string]) {.base.} =
|
method loadLinkPreviews*(self: AccessInterface, urls: seq[string]) {.base.} =
|
||||||
raise newException(ValueError, "No implementation available")
|
raise newException(ValueError, "No implementation available")
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import NimQml, tables
|
import NimQml, tables, sets
|
||||||
import io_interface
|
import io_interface
|
||||||
import ../io_interface as delegate_interface
|
import ../io_interface as delegate_interface
|
||||||
import view, controller
|
import view, controller
|
||||||
|
@ -154,8 +154,8 @@ method addToRecentsGif*(self: Module, item: GifDto) =
|
||||||
method isFavorite*(self: Module, item: GifDto): bool =
|
method isFavorite*(self: Module, item: GifDto): bool =
|
||||||
return self.controller.isFavorite(item)
|
return self.controller.isFavorite(item)
|
||||||
|
|
||||||
method setText*(self: Module, text: string, unfurlUrls: bool) =
|
method setText*(self: Module, text: string, unfurlNewUrls: bool) =
|
||||||
self.controller.setText(text, unfurlUrls)
|
self.controller.setText(text, unfurlNewUrls)
|
||||||
|
|
||||||
method getPlainText*(self: Module): string =
|
method getPlainText*(self: Module): string =
|
||||||
return self.view.getPlainText()
|
return self.view.getPlainText()
|
||||||
|
@ -172,6 +172,9 @@ method setLinkPreviewUrls*(self: Module, urls: seq[string]) =
|
||||||
method linkPreviewsFromCache*(self: Module, urls: seq[string]): Table[string, LinkPreview] =
|
method linkPreviewsFromCache*(self: Module, urls: seq[string]): Table[string, LinkPreview] =
|
||||||
return self.controller.linkPreviewsFromCache(urls)
|
return self.controller.linkPreviewsFromCache(urls)
|
||||||
|
|
||||||
|
method reloadUnfurlingPlan*(self: Module) =
|
||||||
|
self.controller.reloadUnfurlingPlan()
|
||||||
|
|
||||||
method loadLinkPreviews*(self: Module, urls: seq[string]) =
|
method loadLinkPreviews*(self: Module, urls: seq[string]) =
|
||||||
self.controller.loadLinkPreviews(urls)
|
self.controller.loadLinkPreviews(urls)
|
||||||
|
|
||||||
|
|
|
@ -58,10 +58,12 @@ QtObject:
|
||||||
msg: string,
|
msg: string,
|
||||||
replyTo: string,
|
replyTo: string,
|
||||||
contentType: int) {.slot.} =
|
contentType: int) {.slot.} =
|
||||||
|
# FIXME: Update this when `setText` is async.
|
||||||
self.delegate.setText(msg, false)
|
self.delegate.setText(msg, false)
|
||||||
self.delegate.sendChatMessage(msg, replyTo, contentType, self.linkPreviewModel.getUnfuledLinkPreviews())
|
self.delegate.sendChatMessage(msg, replyTo, contentType, self.linkPreviewModel.getUnfuledLinkPreviews())
|
||||||
|
|
||||||
proc sendImages*(self: View, imagePathsAndDataJson: string, msg: string, replyTo: string): string {.slot.} =
|
proc sendImages*(self: View, imagePathsAndDataJson: string, msg: string, replyTo: string): string {.slot.} =
|
||||||
|
# FIXME: Update this when `setText` is async.
|
||||||
self.delegate.setText(msg, false)
|
self.delegate.setText(msg, false)
|
||||||
self.delegate.sendImages(imagePathsAndDataJson, msg, replyTo, self.linkPreviewModel.getUnfuledLinkPreviews())
|
self.delegate.sendImages(imagePathsAndDataJson, msg, replyTo, self.linkPreviewModel.getUnfuledLinkPreviews())
|
||||||
|
|
||||||
|
@ -232,10 +234,7 @@ QtObject:
|
||||||
|
|
||||||
proc setLinkPreviewUrls*(self: View, urls: seq[string]) =
|
proc setLinkPreviewUrls*(self: View, urls: seq[string]) =
|
||||||
self.linkPreviewModel.setUrls(urls)
|
self.linkPreviewModel.setUrls(urls)
|
||||||
if(self.delegate.getLinkPreviewEnabled()):
|
|
||||||
self.updateLinkPreviewsFromCache(urls)
|
self.updateLinkPreviewsFromCache(urls)
|
||||||
else:
|
|
||||||
self.linkPreviewModel.removeAllPreviewData()
|
|
||||||
|
|
||||||
proc clearLinkPreviewCache*(self: View) {.slot.} =
|
proc clearLinkPreviewCache*(self: View) {.slot.} =
|
||||||
self.delegate.clearLinkPreviewCache()
|
self.delegate.clearLinkPreviewCache()
|
||||||
|
@ -254,10 +253,7 @@ QtObject:
|
||||||
|
|
||||||
proc setLinkPreviewEnabledForCurrentMessage(self: View, enabled: bool) {.slot.} =
|
proc setLinkPreviewEnabledForCurrentMessage(self: View, enabled: bool) {.slot.} =
|
||||||
self.delegate.setLinkPreviewEnabledForThisMessage(enabled)
|
self.delegate.setLinkPreviewEnabledForThisMessage(enabled)
|
||||||
let links = self.linkPreviewModel.getLinks()
|
self.delegate.reloadUnfurlingPlan()
|
||||||
self.linkPreviewModel.clearItems()
|
|
||||||
self.setLinkPreviewUrls(links)
|
|
||||||
self.loadLinkPreviews(links)
|
|
||||||
|
|
||||||
proc removeLinkPreviewData*(self: View, index: int) {.slot.} =
|
proc removeLinkPreviewData*(self: View, index: int) {.slot.} =
|
||||||
self.linkPreviewModel.removePreviewData(index)
|
self.linkPreviewModel.removePreviewData(index)
|
||||||
|
|
|
@ -26,3 +26,8 @@ proc `$`*(self: Item): string =
|
||||||
immutable: {self.immutable},
|
immutable: {self.immutable},
|
||||||
linkPreview: {self.linkPreview},
|
linkPreview: {self.linkPreview},
|
||||||
)"""
|
)"""
|
||||||
|
|
||||||
|
proc markAsImmutable*(self: Item) =
|
||||||
|
self.linkPreview = initLinkPreview(self.linkPreview.url)
|
||||||
|
self.unfurled = false
|
||||||
|
self.immutable = true
|
||||||
|
|
|
@ -235,12 +235,13 @@ QtObject:
|
||||||
|
|
||||||
# Move or insert
|
# Move or insert
|
||||||
for i in 0 ..< urls.len:
|
for i in 0 ..< urls.len:
|
||||||
let index = self.findUrlIndex(urls[i])
|
let url = urls[i]
|
||||||
|
let index = self.findUrlIndex(url)
|
||||||
if index >= 0:
|
if index >= 0:
|
||||||
self.moveRow(index, i)
|
self.moveRow(index, i)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
let linkPreview = initLinkPreview(urls[i])
|
let linkPreview = initLinkPreview(url)
|
||||||
var item = Item()
|
var item = Item()
|
||||||
item.unfurled = false
|
item.unfurled = false
|
||||||
item.immutable = false
|
item.immutable = false
|
||||||
|
@ -266,9 +267,7 @@ QtObject:
|
||||||
if index < 0 or index >= self.items.len:
|
if index < 0 or index >= self.items.len:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.items[index].linkPreview = initLinkPreview(self.items[index].linkPreview.url)
|
self.items[index].markAsImmutable()
|
||||||
self.items[index].unfurled = false
|
|
||||||
self.items[index].immutable = true
|
|
||||||
|
|
||||||
let modelIndex = self.createIndex(index, 0, nil)
|
let modelIndex = self.createIndex(index, 0, nil)
|
||||||
defer: modelIndex.delete
|
defer: modelIndex.delete
|
||||||
|
@ -276,7 +275,13 @@ QtObject:
|
||||||
|
|
||||||
proc removeAllPreviewData*(self: Model) {.slot.} =
|
proc removeAllPreviewData*(self: Model) {.slot.} =
|
||||||
for i in 0 ..< self.items.len:
|
for i in 0 ..< self.items.len:
|
||||||
self.removePreviewData(i)
|
self.items[i].markAsImmutable()
|
||||||
|
|
||||||
|
let indexStart = self.createIndex(0, 0, nil)
|
||||||
|
let indexEnd = self.createIndex(self.items.len, 0, nil)
|
||||||
|
defer: indexStart.delete
|
||||||
|
defer: indexEnd.delete
|
||||||
|
self.dataChanged(indexStart, indexEnd)
|
||||||
|
|
||||||
proc getUnfuledLinkPreviews*(self: Model): seq[LinkPreview] =
|
proc getUnfuledLinkPreviews*(self: Model): seq[LinkPreview] =
|
||||||
result = @[]
|
result = @[]
|
||||||
|
|
|
@ -204,6 +204,7 @@ const asyncGetFirstUnseenMessageIdForTaskArg: Task = proc(argEncoded: string) {.
|
||||||
type
|
type
|
||||||
AsyncUnfurlUrlsTaskArg = ref object of QObjectTaskArg
|
AsyncUnfurlUrlsTaskArg = ref object of QObjectTaskArg
|
||||||
urls*: seq[string]
|
urls*: seq[string]
|
||||||
|
requestUuid*: string
|
||||||
|
|
||||||
const asyncUnfurlUrlsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
const asyncUnfurlUrlsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||||
let arg = decode[AsyncUnfurlUrlsTaskArg](argEncoded)
|
let arg = decode[AsyncUnfurlUrlsTaskArg](argEncoded)
|
||||||
|
@ -212,7 +213,8 @@ const asyncUnfurlUrlsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||||
let output = %*{
|
let output = %*{
|
||||||
"error": (if response.error != nil: response.error.message else: ""),
|
"error": (if response.error != nil: response.error.message else: ""),
|
||||||
"response": response.result,
|
"response": response.result,
|
||||||
"requestedUrls": %*arg.urls
|
"requestedUrls": %*arg.urls,
|
||||||
|
"requestUuid": arg.requestUuid
|
||||||
}
|
}
|
||||||
arg.finish(output)
|
arg.finish(output)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
@ -220,7 +222,8 @@ const asyncUnfurlUrlsTask: Task = proc(argEncoded: string) {.gcsafe, nimcall.} =
|
||||||
let output = %*{
|
let output = %*{
|
||||||
"error": e.msg,
|
"error": e.msg,
|
||||||
"response": "",
|
"response": "",
|
||||||
"requestedUrls": %*arg.urls
|
"requestedUrls": %*arg.urls,
|
||||||
|
"requestUuid": arg.requestUuid
|
||||||
}
|
}
|
||||||
arg.finish(output)
|
arg.finish(output)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,71 @@
|
||||||
|
import json, strformat, chronicles, Tables
|
||||||
|
include ../../../common/json_utils
|
||||||
|
|
||||||
|
type
|
||||||
|
UrlUnfurlingPermission* {.pure.} = enum
|
||||||
|
UrlUnfurlingAllowed = 0
|
||||||
|
UrlUnfurlingAskUser
|
||||||
|
UrlUnfurlingForbiddenBySettings
|
||||||
|
UrlUnfurlingNotSupported
|
||||||
|
|
||||||
|
UrlUnfurlingMetadata* = ref object
|
||||||
|
url*: string
|
||||||
|
permission*: UrlUnfurlingPermission
|
||||||
|
isStatusSharedUrl*: bool
|
||||||
|
|
||||||
|
UrlsUnfurlingPlan* = ref object
|
||||||
|
urls*: seq[UrlUnfurlingMetadata]
|
||||||
|
|
||||||
|
proc initUrlsUnfurlingPlan*(): UrlsUnfurlingPlan =
|
||||||
|
result = UrlsUnfurlingPlan()
|
||||||
|
result.urls = newSeq[UrlUnfurlingMetadata]()
|
||||||
|
|
||||||
|
proc toUrlUnfurlingPermission*(value: int): UrlUnfurlingPermission =
|
||||||
|
try:
|
||||||
|
return UrlUnfurlingPermission(value)
|
||||||
|
except RangeDefect:
|
||||||
|
return UrlUnfurlingPermission.UrlUnfurlingForbiddenBySettings
|
||||||
|
|
||||||
|
proc toUrlUnfurlingMetadata*(jsonObj: JsonNode): UrlUnfurlingMetadata =
|
||||||
|
result = UrlUnfurlingMetadata()
|
||||||
|
|
||||||
|
if jsonObj.kind != JObject:
|
||||||
|
warn "node is not an object", source = "toUrlUnfurlingMetadata"
|
||||||
|
return
|
||||||
|
|
||||||
|
result.url = jsonObj["url"].getStr()
|
||||||
|
result.permission = toUrlUnfurlingPermission(jsonObj["permission"].getInt)
|
||||||
|
result.isStatusSharedUrl = jsonObj["isStatusSharedURL"].getBool()
|
||||||
|
|
||||||
|
proc toUrlUnfurlingPlan*(jsonObj: JsonNode): UrlsUnfurlingPlan =
|
||||||
|
result = UrlsUnfurlingPlan()
|
||||||
|
|
||||||
|
if jsonObj.kind != JObject:
|
||||||
|
warn "node is not an object", source = "toUrlUnfurlingPlan"
|
||||||
|
return
|
||||||
|
|
||||||
|
let urlsSeq = jsonObj["urls"]
|
||||||
|
|
||||||
|
if urlsSeq.kind != JArray:
|
||||||
|
warn "urls is not an array", source = "toUrlUnfurlingPlan"
|
||||||
|
return
|
||||||
|
|
||||||
|
for metadata in urlsSeq:
|
||||||
|
result.urls.add(toUrlUnfurlingMetadata(metadata))
|
||||||
|
|
||||||
|
proc `$`*(self: UrlUnfurlingMetadata): string =
|
||||||
|
if self == nil:
|
||||||
|
return "nil"
|
||||||
|
return fmt"""UrlUnfurlingMetadata( permission: {self.permission}, isStatusSharedUrl: {self.isStatusSharedUrl} )"""
|
||||||
|
|
||||||
|
proc `$`*(self: UrlsUnfurlingPlan): string =
|
||||||
|
if self == nil:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
var rows = ""
|
||||||
|
for url, metadata in self.urls:
|
||||||
|
rows = fmt"""{rows}
|
||||||
|
url: {url}, metadata: {metadata}"""
|
||||||
|
|
||||||
|
result = fmt"""UrlsUnfurlingPlan({rows}
|
||||||
|
)"""
|
|
@ -19,6 +19,7 @@ import ./dto/pinned_message_update as pinned_msg_update_dto
|
||||||
import ./dto/removed_message as removed_msg_dto
|
import ./dto/removed_message as removed_msg_dto
|
||||||
import ./dto/link_preview
|
import ./dto/link_preview
|
||||||
import ./dto/status_link_preview
|
import ./dto/status_link_preview
|
||||||
|
import ./dto/urls_unfurling_plan
|
||||||
import ./message_cursor
|
import ./message_cursor
|
||||||
|
|
||||||
import ../../common/message as message_common
|
import ../../common/message as message_common
|
||||||
|
@ -120,8 +121,9 @@ type
|
||||||
chatId*: string
|
chatId*: string
|
||||||
message*: MessageDto
|
message*: MessageDto
|
||||||
|
|
||||||
LinkPreviewV2DataArgs* = ref object of Args
|
LinkPreviewDataArgs* = ref object of Args
|
||||||
linkPreviews*: Table[string, LinkPreview]
|
linkPreviews*: Table[string, LinkPreview]
|
||||||
|
requestUuid*: string
|
||||||
|
|
||||||
ReloadMessagesArgs* = ref object of Args
|
ReloadMessagesArgs* = ref object of Args
|
||||||
communityId*: string
|
communityId*: string
|
||||||
|
@ -821,8 +823,14 @@ QtObject:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error "getTextUrls failed", errName = e.name, errDesription = e.msg
|
error "getTextUrls failed", errName = e.name, errDesription = e.msg
|
||||||
|
|
||||||
proc onAsyncUnfurlUrlsFinished*(self: Service, response: string) {.slot.}=
|
proc getTextURLsToUnfurl*(self: Service, text: string): UrlsUnfurlingPlan =
|
||||||
|
try:
|
||||||
|
let response = status_go.getTextURLsToUnfurl(text)
|
||||||
|
return toUrlUnfurlingPlan(response.result)
|
||||||
|
except Exception as e:
|
||||||
|
error "getTextURLsToUnfurl failed", errName = e.name, errDesription = e.msg
|
||||||
|
|
||||||
|
proc onAsyncUnfurlUrlsFinished*(self: Service, response: string) {.slot.}=
|
||||||
let responseObj = response.parseJson
|
let responseObj = response.parseJson
|
||||||
if responseObj.kind != JObject:
|
if responseObj.kind != JObject:
|
||||||
warn "expected response is not a json object", methodName = "onAsyncUnfurlUrlsFinished"
|
warn "expected response is not a json object", methodName = "onAsyncUnfurlUrlsFinished"
|
||||||
|
@ -858,22 +866,25 @@ QtObject:
|
||||||
if not linkPreviews.hasKey(url):
|
if not linkPreviews.hasKey(url):
|
||||||
linkPreviews[url] = initLinkPreview(url)
|
linkPreviews[url] = initLinkPreview(url)
|
||||||
|
|
||||||
let args = LinkPreviewV2DataArgs(
|
let args = LinkPreviewDataArgs(
|
||||||
linkPreviews: linkPreviews
|
linkPreviews: linkPreviews,
|
||||||
|
requestUuid: responseObj["requestUuid"].getStr
|
||||||
)
|
)
|
||||||
self.events.emit(SIGNAL_URLS_UNFURLED, args)
|
self.events.emit(SIGNAL_URLS_UNFURLED, args)
|
||||||
|
|
||||||
|
proc asyncUnfurlUrls*(self: Service, urls: seq[string]): string =
|
||||||
proc asyncUnfurlUrls*(self: Service, urls: seq[string]) =
|
|
||||||
if len(urls) == 0:
|
if len(urls) == 0:
|
||||||
return
|
return ""
|
||||||
|
let uuid = $genUUID()
|
||||||
let arg = AsyncUnfurlUrlsTaskArg(
|
let arg = AsyncUnfurlUrlsTaskArg(
|
||||||
tptr: cast[ByteAddress](asyncUnfurlUrlsTask),
|
tptr: cast[ByteAddress](asyncUnfurlUrlsTask),
|
||||||
vptr: cast[ByteAddress](self.vptr),
|
vptr: cast[ByteAddress](self.vptr),
|
||||||
slot: "onAsyncUnfurlUrlsFinished",
|
slot: "onAsyncUnfurlUrlsFinished",
|
||||||
urls: urls
|
urls: urls,
|
||||||
|
requestUuid: uuid,
|
||||||
)
|
)
|
||||||
self.threadpool.start(arg)
|
self.threadpool.start(arg)
|
||||||
|
return uuid
|
||||||
|
|
||||||
# See render-inline in status-mobile/src/status_im/ui/screens/chat/message/message.cljs
|
# See render-inline in status-mobile/src/status_im/ui/screens/chat/message/message.cljs
|
||||||
proc renderInline(self: Service, parsedText: ParsedText, communityChats: seq[ChatDto]): string =
|
proc renderInline(self: Service, parsedText: ParsedText, communityChats: seq[ChatDto]): string =
|
||||||
|
|
|
@ -76,6 +76,10 @@ proc getTextUrls*(text: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
let payload = %*[text]
|
let payload = %*[text]
|
||||||
result = callPrivateRPC("getTextURLs".prefix, payload)
|
result = callPrivateRPC("getTextURLs".prefix, payload)
|
||||||
|
|
||||||
|
proc getTextURLsToUnfurl*(text: string): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
|
let payload = %*[text]
|
||||||
|
result = callPrivateRPC("getTextURLsToUnfurl".prefix, payload)
|
||||||
|
|
||||||
proc unfurlUrls*(urls: seq[string]): RpcResponse[JsonNode] {.raises: [Exception].} =
|
proc unfurlUrls*(urls: seq[string]): RpcResponse[JsonNode] {.raises: [Exception].} =
|
||||||
let payload = %*[urls]
|
let payload = %*[urls]
|
||||||
result = callPrivateRPC("unfurlURLs".prefix, payload)
|
result = callPrivateRPC("unfurlURLs".prefix, payload)
|
||||||
|
|
|
@ -159,7 +159,7 @@ Control {
|
||||||
sourceModel: root.linkPreviewModel
|
sourceModel: root.linkPreviewModel
|
||||||
filters: [
|
filters: [
|
||||||
ExpressionFilter {
|
ExpressionFilter {
|
||||||
expression: { return !model.immutable || model.unfurled } // Filter out immutable links that haven't been unfurled yet
|
expression: !model.immutable || model.unfurled // Filter out immutable links that haven't been unfurled yet
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 2c55b9c676d71f75dfd07e0973e168c88cc93954
|
Subproject commit a51f8aa13c20609c894ea36a9469ce3f57645657
|
Loading…
Reference in New Issue