fix(LinkPreviews): Fixing gif hyperlink detection in StatusChatInput

The link preview model can be filtered, but the hyperlink detection needs an unfiltered model to properly highlight all URLs. This brought in the need to separate the urls model and the link previews model.
This commit is contained in:
Alex Jbanca 2023-10-23 19:21:35 +03:00 committed by Alex Jbanca
parent b9867c2463
commit 8ac6eb8916
8 changed files with 136 additions and 11 deletions

View File

@ -111,7 +111,7 @@ proc setLinkPreviewEnabledForThisMessage*(self: Controller, enabled: bool) =
self.delegate.setAskToEnableLinkPreview(false) self.delegate.setAskToEnableLinkPreview(false)
proc resetLinkPreviews(self: Controller) = proc resetLinkPreviews(self: Controller) =
self.delegate.setUrls(@[]) self.delegate.setLinkPreviewUrls(@[])
self.linkPreviewCache.clear() self.linkPreviewCache.clear()
self.linkPreviewCurrentMessageSetting = self.linkPreviewPersistentSetting self.linkPreviewCurrentMessageSetting = self.linkPreviewPersistentSetting
self.delegate.setAskToEnableLinkPreview(false) self.delegate.setAskToEnableLinkPreview(false)
@ -201,12 +201,15 @@ proc canAskToEnableLinkPreview(self: Controller): bool =
proc setText*(self: Controller, text: string, unfurlNewUrls: bool) = proc setText*(self: Controller, text: string, unfurlNewUrls: bool) =
if text == "": if text == "":
self.resetLinkPreviews() self.resetLinkPreviews()
self.delegate.setUrls(@[])
return return
let urls = self.messageService.getTextUrls(text) let urls = self.messageService.getTextUrls(text)
self.delegate.setUrls(urls)
let supportedUrls = urls.filter(x => not x.endsWith(".gif")) # GIFs are currently unfurled by receiver let supportedUrls = urls.filter(x => not x.endsWith(".gif")) # GIFs are currently unfurled by receiver
self.delegate.setUrls(supportedUrls) self.delegate.setLinkPreviewUrls(supportedUrls)
let newUrls = self.linkPreviewCache.unknownUrls(urls) let newUrls = self.linkPreviewCache.unknownUrls(supportedUrls)
let askToEnableLinkPreview = len(newUrls) > 0 and self.canAskToEnableLinkPreview() let askToEnableLinkPreview = len(newUrls) > 0 and self.canAskToEnableLinkPreview()
self.delegate.setAskToEnableLinkPreview(askToEnableLinkPreview) self.delegate.setAskToEnableLinkPreview(askToEnableLinkPreview)

View File

@ -102,7 +102,7 @@ method setText*(self: AccessInterface, text: string, unfurlUrls: bool) {.base.}
method getPlainText*(self: AccessInterface): string = method getPlainText*(self: AccessInterface): string =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method setUrls*(self: AccessInterface, urls: seq[string]) {.base.} = method setLinkPreviewUrls*(self: AccessInterface, urls: seq[string]) {.base.} =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method updateLinkPreviewsFromCache*(self: AccessInterface, urls: seq[string]) {.base.} = method updateLinkPreviewsFromCache*(self: AccessInterface, urls: seq[string]) {.base.} =
@ -128,3 +128,6 @@ method setAskToEnableLinkPreview*(self: AccessInterface, value: bool) =
method setLinkPreviewEnabledForThisMessage*(self: AccessInterface, enabled: bool) = method setLinkPreviewEnabledForThisMessage*(self: AccessInterface, enabled: bool) =
raise newException(ValueError, "No implementation available") raise newException(ValueError, "No implementation available")
method setUrls*(self: AccessInterface, urls: seq[string]) {.base.} =
raise newException(ValueError, "No implementation available")

View File

@ -166,8 +166,8 @@ method clearLinkPreviewCache*(self: Module) {.slot.} =
method updateLinkPreviewsFromCache*(self: Module, urls: seq[string]) = method updateLinkPreviewsFromCache*(self: Module, urls: seq[string]) =
self.view.updateLinkPreviewsFromCache(urls) self.view.updateLinkPreviewsFromCache(urls)
method setUrls*(self: Module, urls: seq[string]) = method setLinkPreviewUrls*(self: Module, urls: seq[string]) =
self.view.setUrls(urls) self.view.setLinkPreviewUrls(urls)
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)
@ -186,3 +186,6 @@ method setAskToEnableLinkPreview*(self: Module, value: bool) =
method setLinkPreviewEnabledForThisMessage*(self: Module, value: bool) = method setLinkPreviewEnabledForThisMessage*(self: Module, value: bool) =
self.controller.setLinkPreviewEnabledForThisMessage(value) self.controller.setLinkPreviewEnabledForThisMessage(value)
method setUrls*(self: Module, urls: seq[string]) =
self.view.setUrls(urls)

View File

@ -0,0 +1,94 @@
import NimQml, tables, sequtils
type
ModelRole {.pure.} = enum
Url = UserRole + 1
QtObject:
type
Model* = ref object of QAbstractListModel
items: seq[string]
proc delete*(self: Model) =
self.items = @[]
self.QAbstractListModel.delete
proc setup(self: Model) =
self.QAbstractListModel.setup
proc newUrlsModel*(): Model =
new(result, delete)
result.setup
proc countChanged(self: Model) {.signal.}
proc getCount*(self: Model): int {.slot.} =
self.items.len
QtProperty[int] count:
read = getCount
notify = countChanged
method rowCount(self: Model, index: QModelIndex = nil): int =
return self.items.len
method roleNames(self: Model): Table[int, string] =
{
ModelRole.Url.int:"url"
}.toTable
method data(self: Model, index: QModelIndex, role: int): QVariant =
if (not index.isValid):
return
if (index.row < 0 or index.row >= self.items.len):
return
let enumRole = role.ModelRole
case enumRole:
of ModelRole.Url:
let item = self.items[index.row]
result = newQVariant(item)
else:
result = newQVariant()
proc removeItemWithIndex(self: Model, ind: int) =
if(ind < 0 or ind >= self.items.len):
return
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginRemoveRows(parentModelIndex, ind, ind)
self.items.delete(ind)
self.endRemoveRows()
proc setUrls*(self: Model, urls: seq[string]) =
var itemsToInsert: seq[string]
var indexesToRemove: seq[int]
#remove
for i in 0 ..< self.items.len:
if not urls.anyIt(it == self.items[i]):
indexesToRemove.add(i)
while indexesToRemove.len > 0:
let index = pop(indexesToRemove)
self.removeItemWithIndex(index)
# Move or insert
for i in 0 ..< urls.len:
if self.items.anyIt(it == urls[i]):
continue
itemsToInsert.add(urls[i])
if itemsToInsert.len > 0:
let parentModelIndex = newQModelIndex()
defer: parentModelIndex.delete
self.beginInsertRows(parentModelIndex, self.items.len, self.items.len + itemsToInsert.len - 1)
self.items = self.items & itemsToInsert
self.endInsertRows()
self.countChanged()

View File

@ -2,6 +2,7 @@ import NimQml
import ./io_interface import ./io_interface
import ./gif_column_model import ./gif_column_model
import ./preserved_properties import ./preserved_properties
import ./urls_model
import ../../../../../../app/modules/shared_models/link_preview_model as link_preview_model import ../../../../../../app/modules/shared_models/link_preview_model as link_preview_model
import ../../../../../../app_service/service/gif/dto import ../../../../../../app_service/service/gif/dto
@ -17,6 +18,8 @@ QtObject:
preservedPropertiesVariant: QVariant preservedPropertiesVariant: QVariant
linkPreviewModel: link_preview_model.Model linkPreviewModel: link_preview_model.Model
linkPreviewModelVariant: QVariant linkPreviewModelVariant: QVariant
urlsModel: urls_model.Model
urlsModelVariant: QVariant
askToEnableLinkPreview: bool askToEnableLinkPreview: bool
proc delete*(self: View) = proc delete*(self: View) =
@ -24,10 +27,12 @@ QtObject:
self.gifColumnAModel.delete self.gifColumnAModel.delete
self.gifColumnBModel.delete self.gifColumnBModel.delete
self.gifColumnCModel.delete self.gifColumnCModel.delete
self.preservedProperties.delete
self.preservedPropertiesVariant.delete self.preservedPropertiesVariant.delete
self.linkPreviewModel.delete self.preservedProperties.delete
self.linkPreviewModelVariant.delete self.linkPreviewModelVariant.delete
self.linkPreviewModel.delete
self.urlsModelVariant.delete
self.urlsModel.delete
proc newView*(delegate: io_interface.AccessInterface): View = proc newView*(delegate: io_interface.AccessInterface): View =
new(result, delete) new(result, delete)
@ -41,6 +46,8 @@ QtObject:
result.preservedPropertiesVariant = newQVariant(result.preservedProperties) result.preservedPropertiesVariant = newQVariant(result.preservedProperties)
result.linkPreviewModel = newLinkPreviewModel() result.linkPreviewModel = newLinkPreviewModel()
result.linkPreviewModelVariant = newQVariant(result.linkPreviewModel) result.linkPreviewModelVariant = newQVariant(result.linkPreviewModel)
result.urlsModel = newUrlsModel()
result.urlsModelVariant = newQVariant(result.urlsModel)
result.askToEnableLinkPreview = false result.askToEnableLinkPreview = false
proc load*(self: View) = proc load*(self: View) =
@ -223,7 +230,7 @@ QtObject:
let linkPreviews = self.delegate.linkPreviewsFromCache(urls) let linkPreviews = self.delegate.linkPreviewsFromCache(urls)
self.linkPreviewModel.updateLinkPreviews(linkPreviews) self.linkPreviewModel.updateLinkPreviews(linkPreviews)
proc setUrls*(self: View, urls: seq[string]) = proc setLinkPreviewUrls*(self: View, urls: seq[string]) =
self.linkPreviewModel.setUrls(urls) self.linkPreviewModel.setUrls(urls)
if(self.delegate.getLinkPreviewEnabled()): if(self.delegate.getLinkPreviewEnabled()):
self.updateLinkPreviewsFromCache(urls) self.updateLinkPreviewsFromCache(urls)
@ -253,8 +260,19 @@ QtObject:
self.delegate.setLinkPreviewEnabledForThisMessage(enabled) self.delegate.setLinkPreviewEnabledForThisMessage(enabled)
let links = self.linkPreviewModel.getLinks() let links = self.linkPreviewModel.getLinks()
self.linkPreviewModel.clearItems() self.linkPreviewModel.clearItems()
self.setUrls(links) self.setLinkPreviewUrls(links)
self.loadLinkPreviews(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)
proc urlsModelChanged(self: View) {.signal.}
proc getUrlsModel*(self: View): QVariant {.slot.} =
return self.urlsModelVariant
proc setUrls*(self: View, urls: seq[string]) =
self.urlsModel.setUrls(urls)
QtProperty[QVariant] urlsModel:
read = getUrlsModel
notify = urlsModelChanged

View File

@ -112,6 +112,7 @@ SplitView {
enabled: enabledCheckBox.checked enabled: enabledCheckBox.checked
linkPreviewModel: fakeLinksModel linkPreviewModel: fakeLinksModel
urlsModel: fakeLinksModel
askToEnableLinkPreview: askToEnableLinkPreviewSwitch.checked askToEnableLinkPreview: askToEnableLinkPreviewSwitch.checked
onAskToEnableLinkPreviewChanged: { onAskToEnableLinkPreviewChanged: {
if(askToEnableLinkPreview) { if(askToEnableLinkPreview) {

View File

@ -255,6 +255,7 @@ Item {
store: root.rootStore store: root.rootStore
usersStore: d.activeUsersStore usersStore: d.activeUsersStore
linkPreviewModel: d.activeChatContentModule.inputAreaModule.linkPreviewModel linkPreviewModel: d.activeChatContentModule.inputAreaModule.linkPreviewModel
urlsModel: d.activeChatContentModule.inputAreaModule.urlsModel
askToEnableLinkPreview: { askToEnableLinkPreview: {
if(!d.activeChatContentModule || !d.activeChatContentModule.inputAreaModule || !d.activeChatContentModule.inputAreaModule.preservedProperties) if(!d.activeChatContentModule || !d.activeChatContentModule.inputAreaModule || !d.activeChatContentModule.inputAreaModule.preservedProperties)
return false return false

View File

@ -65,6 +65,8 @@ Rectangle {
property var linkPreviewModel: null property var linkPreviewModel: null
property var urlsModel: null
property bool askToEnableLinkPreview: false property bool askToEnableLinkPreview: false
property var imageErrorMessageLocation: StatusChatInput.ImageErrorMessageLocation.Top // TODO: Remove this property? property var imageErrorMessageLocation: StatusChatInput.ImageErrorMessageLocation.Top // TODO: Remove this property?
@ -1366,7 +1368,7 @@ Rectangle {
TextEditHyperlinksFormatter { TextEditHyperlinksFormatter {
id: hyperlinksFormatter id: hyperlinksFormatter
textEdit: messageInputField textEdit: messageInputField
urlModel: control.linkPreviewModel urlModel: control.urlsModel
highlightUrl: linkPreviewArea.hoveredUrl highlightUrl: linkPreviewArea.hoveredUrl
enabled: messageInputField.enabled && messageInputField.textFormat == TextEdit.RichText enabled: messageInputField.enabled && messageInputField.textFormat == TextEdit.RichText
} }