feat: Generate link previews in StatusChatInput - introduce remove/reload link to link preview model

1. Updating the link preview model to allow remove URL data. This will discard the link preview data and mark the item as immutable. When an item is immutable it won't accept further updates.

2. Remove reset model when the model urls is changing. Use insert/update/remove rows to avoid any unnecessary UI updates and visual artefacts.

2. Add reload link API. It will request again the link preview
This commit is contained in:
Alex Jbanca 2023-09-26 17:08:38 +03:00 committed by Alex Jbanca
parent 2abe0358fc
commit ebe102ac8a
6 changed files with 116 additions and 20 deletions

View File

@ -184,3 +184,9 @@ proc clearLinkPreviewCache*(self: Controller) =
proc onUrlsUnfurled(self: Controller, args: LinkPreviewV2DataArgs) =
let urls = self.linkPreviewCache.add(args.linkPreviews)
self.delegate.updateLinkPreviewsFromCache(urls)
proc reloadLinkPreview*(self: Controller, url: string) =
if url.len == 0:
return
self.messageService.asyncUnfurlUrls(@[url])

View File

@ -110,3 +110,6 @@ method clearLinkPreviewCache*(self: AccessInterface) {.base.} =
method linkPreviewsFromCache*(self: AccessInterface, urls: seq[string]): Table[string, LinkPreview] {.base.} =
raise newException(ValueError, "No implementation available")
method reloadLinkPreview*(self: AccessInterface, url: string) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -165,3 +165,6 @@ method setUrls*(self: Module, urls: seq[string]) =
method linkPreviewsFromCache*(self: Module, urls: seq[string]): Table[string, LinkPreview] =
return self.controller.linkPreviewsFromCache(urls)
method reloadLinkPreview*(self: Module, url: string) =
self.controller.reloadLinkPreview(url)

View File

@ -212,3 +212,8 @@ QtObject:
proc clearLinkPreviewCache*(self: View) {.slot.} =
self.delegate.clearLinkPreviewCache()
proc removeLinkPreview(self: View, link: string) {.slot.} =
self.linkPreviewModel.removePreviewData(link)
proc reloadLinkPreview(self: View, link: string) {.slot.} =
self.delegate.reloadLinkPreview(link)

View File

@ -4,6 +4,7 @@ import ../../../app_service/service/message/dto/link_preview
type
Item* = ref object
unfurled*: bool
immutable*: bool
linkPreview*: LinkPreview
proc delete*(self: Item) =
@ -18,5 +19,6 @@ proc `linkPreview=`*(self: Item, linkPreview: LinkPreview) {.inline.} =
proc `$`*(self: Item): string =
result = fmt"""LinkPreviewItem(
unfurled: {self.unfurled},
immutable: {self.immutable},
linkPreview: {self.linkPreview},
)"""

View File

@ -1,4 +1,4 @@
import NimQml, strformat, tables
import NimQml, strformat, tables, sequtils
import ./link_preview_item
import ../../../app_service/service/message/dto/link_preview
@ -6,6 +6,7 @@ type
ModelRole {.pure.} = enum
Url = UserRole + 1
Unfurled
Immutable
Hostname
Title
Description
@ -64,6 +65,7 @@ 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",
@ -89,6 +91,8 @@ QtObject:
result = newQVariant(item.linkPreview.url)
of ModelRole.Unfurled:
result = newQVariant(item.unfurled)
of ModelRole.Immutable:
result = newQVariant(item.immutable)
of ModelRole.Hostname:
result = newQVariant(item.linkPreview.hostname)
of ModelRole.Title:
@ -105,33 +109,106 @@ QtObject:
result = newQVariant(item.linkPreview.thumbnail.url)
of ModelRole.ThumbnailDataUri:
result = newQVariant(item.linkPreview.thumbnail.dataUri)
proc clearItems*(self: Model) =
self.beginResetModel()
self.items = @[]
self.endResetModel()
self.countChanged()
proc setUrls*(self: Model, urls: seq[string]) =
var items: seq[Item]
for url in urls:
let linkPreview = initLinkPreview(url)
var item = Item()
item.unfurled = false
item.linkPreview = linkPreview
items.add(item)
proc urlExists(self: Model, url: string): bool =
for it in self.items:
if(it.linkPreview.url == url):
return true
return false
self.beginResetModel()
self.items = items
self.endResetModel()
self.countChanged()
proc urlExists(self: seq[string], url: string): bool =
for it in self:
if(it == url):
return true
return false
proc removeItemWithIndex(self: Model, ind: int) =
if(ind == -1 or ind >= self.items.len):
return
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginRemoveRows(parentModelIndex, ind, ind)
self.items.delete(ind)
self.endRemoveRows()
proc getItemAtIndex*(self: Model, index: int): Item =
if(index < 0 or index >= self.items.len):
return
return self.items[index]
proc findUrlIndex(self: Model, url: string): int =
for i in 0 ..< self.items.len:
if(self.items[i].linkPreview.url == url):
return i
return -1
proc updateLinkPreviews*(self: Model, linkPreviews: Table[string, LinkPreview]) =
for row, item in self.items:
if not linkPreviews.hasKey(item.linkPreview.url):
if not linkPreviews.hasKey(item.linkPreview.url) or item.immutable:
continue
item.unfurled = true
item.linkPreview = linkPreviews[item.linkPreview.url]
let modelIndex = self.createIndex(row, 0, nil)
defer: modelIndex.delete
self.dataChanged(modelIndex, modelIndex)
proc setUrls*(self: Model, urls: seq[string]) =
var itemsToInsert: seq[Item]
var itemsToUpdate: Table[string, LinkPreview]
var indexesToRemove: seq[int]
#remove
for i in 0 ..< self.items.len:
if not urls.urlExists(self.items[i].linkPreview.url):
indexesToRemove.add(i)
while indexesToRemove.len > 0:
let index = pop(indexesToRemove)
self.removeItemWithIndex(index)
self.countChanged()
# Update or insert
for url in urls:
let linkPreview = initLinkPreview(url)
if(self.urlExists(url)):
itemsToUpdate[url] = linkPreview
else:
var item = Item()
item.unfurled = false
item.immutable = false
item.linkPreview = linkPreview
itemsToInsert.add(item)
#update
if(itemsToUpdate.len > 0):
self.updateLinkPreviews(itemsToUpdate)
#insert
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len + itemsToInsert.len - 1)
self.items = concat(self.items, itemsToInsert)
self.endInsertRows()
self.countChanged()
proc clearItems*(self: Model) =
self.beginResetModel()
self.items = @[]
self.endResetModel()
self.countChanged()
proc removePreviewData*(self: Model, link: string) =
let index = self.findUrlIndex(link)
if index < 0:
return
self.items[index].linkPreview = initLinkPreview(link)
self.items[index].unfurled = false
self.items[index].immutable = true
let modelIndex = self.createIndex(index, 0, nil)
defer: modelIndex.delete
self.dataChanged(modelIndex, modelIndex)