fix(@desktop/chat): mention suggestion list fixed

This commit is contained in:
Sale Djenic 2022-02-08 13:08:02 +01:00 committed by Iuri Matias
parent 485d6a370d
commit aa6036b78b
12 changed files with 126 additions and 50 deletions

View File

@ -0,0 +1,35 @@
import sequtils, strutils, sugar, re
import ../service/contacts/dto/contacts
proc replaceMentionsWithPubKeys*(allKnownContacts: seq[ContactsDto], message: string): string =
let aliasPattern = re(r"(@[A-z][a-z]+ [A-z][a-z]* [A-z][a-z]*)", flags = {reStudy, reIgnoreCase})
let ensPattern = re(r"(@\w+(?=(\.stateofus)?\.eth))", flags = {reStudy, reIgnoreCase})
let namePattern = re(r"(@\w+)", flags = {reStudy, reIgnoreCase})
let aliasMentions = findAll(message, aliasPattern)
let ensMentions = findAll(message, ensPattern)
let nameMentions = findAll(message, namePattern)
var updatedMessage = message
# In the following lines we're free to compare to `x.userNameOrAlias()` cause that's actually what we're displaying
# in the mentions suggestion list.
for mention in aliasMentions:
let listOfMatched = allKnownContacts.filter(x => "@" & x.userNameOrAlias().toLowerAscii == mention.toLowerAscii)
echo "EX1: mention: ", mention, " list: ", repr(listOfMatched)
if(listOfMatched.len > 0):
updatedMessage = updatedMessage.replaceWord(mention, '@' & listOfMatched[0].id)
for mention in ensMentions:
let listOfMatched = allKnownContacts.filter(x => "@" & x.userNameOrAlias().toLowerAscii == mention.toLowerAscii)
echo "EX2: mention: ", mention, " list: ", repr(listOfMatched)
if(listOfMatched.len > 0):
updatedMessage = updatedMessage.replaceWord(mention, '@' & listOfMatched[0].id)
for mention in nameMentions:
let listOfMatched = allKnownContacts.filter(x => x.userNameOrAlias().toLowerAscii == mention.toLowerAscii or
"@" & x.userNameOrAlias().toLowerAscii == mention.toLowerAscii)
echo "EX3: mention: ", mention, " list: ", repr(listOfMatched)
if(listOfMatched.len > 0):
updatedMessage = updatedMessage.replaceWord(mention, '@' & listOfMatched[0].id)
return updatedMessage

View File

@ -10,6 +10,7 @@ import ../../../app/global/global_singleton
import ../../../app/core/eventemitter import ../../../app/core/eventemitter
import ../../../constants import ../../../constants
import ../../common/message as message_common
from ../../common/account_constants import ZERO_ADDRESS from ../../common/account_constants import ZERO_ADDRESS
export chat_dto export chat_dto
@ -271,9 +272,12 @@ QtObject:
preferredUsername: string = "", preferredUsername: string = "",
communityId: string = "") = communityId: string = "") =
try: try:
let allKnownContacts = self.contactService.getContacts()
let processedMsg = message_common.replaceMentionsWithPubKeys(allKnownContacts, msg)
let response = status_chat.sendChatMessage( let response = status_chat.sendChatMessage(
chatId, chatId,
msg, processedMsg,
replyTo, replyTo,
contentType, contentType,
preferredUsername, preferredUsername,

View File

@ -13,6 +13,8 @@ import ../chat/dto/chat as chat_dto
import ./dto/pinned_message_update as pinned_msg_update_dto 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 ../../common/message as message_common
export message_dto export message_dto
export pinned_msg_dto export pinned_msg_dto
export reaction_dto export reaction_dto
@ -652,9 +654,12 @@ proc deleteMessage*(self: Service, messageId: string) =
except Exception as e: except Exception as e:
error "error: ", methodName="deleteMessage", errName = e.name, errDesription = e.msg error "error: ", methodName="deleteMessage", errName = e.name, errDesription = e.msg
proc editMessage*(self: Service, messageId: string, updatedMsg: string) = proc editMessage*(self: Service, messageId: string, msg: string) =
try: try:
let response = status_go.editMessage(messageId, updatedMsg) let allKnownContacts = self.contactService.getContacts()
let processedMsg = message_common.replaceMentionsWithPubKeys(allKnownContacts, msg)
let response = status_go.editMessage(messageId, processedMsg)
var messagesArr: JsonNode var messagesArr: JsonNode
var messages: seq[MessageDto] var messages: seq[MessageDto]

View File

@ -33,7 +33,7 @@ import shared.panels 1.0
Rectangle { Rectangle {
id: container id: container
property QtObject model property var model
property Item delegate property Item delegate
property alias suggestionsModel: filterItem.model property alias suggestionsModel: filterItem.model
property alias filter: filterItem.filter property alias filter: filterItem.filter
@ -193,8 +193,8 @@ Rectangle {
anchors.leftMargin: Style.current.smallPadding anchors.leftMargin: Style.current.smallPadding
image.width: 32 image.width: 32
image.height: 32 image.height: 32
image.source: model.identicon image.source: model.icon
image.isIdenticon: true image.isIdenticon: model.isIdenticon
} }
StyledText { StyledText {

View File

@ -2,11 +2,11 @@ import QtQuick 2.13
import utils 1.0 import utils 1.0
Item { Item {
id: component id: suggestionsPanelRoot
property alias model: filterModel property alias model: filterModel
property string formattedFilter property string formattedFilter
property QtObject sourceModel: undefined property var sourceModel
property string filter: "" property string filter: ""
property int cursorPosition: 0 property int cursorPosition: 0
property int lastAtPosition: 0 property int lastAtPosition: 0
@ -17,6 +17,23 @@ Item {
onSourceModelChanged: invalidateFilter() onSourceModelChanged: invalidateFilter()
Component.onCompleted: invalidateFilter() Component.onCompleted: invalidateFilter()
ListView {
// This is a fake list (invisible), used just for the sake of accessing items of the `sourceModel`
// without exposing explicit methods from the model which would return item detail.
// In general the whole thing about preparing/displaying suggestion panel and list there should
// be handled in a much better way, at least using `ListView` and `DelegateModel` which will
// filter out the list instead doing all that manually here.
id: sourceModelList
visible: false
model: suggestionsPanelRoot.sourceModel
delegate: Item {
property string publicKey: model.id
property string name: model.name
property string icon: model.icon
property bool isIdenticon: model.isIdenticon
}
}
ListModel { ListModel {
id: filterModel id: filterModel
} }
@ -43,14 +60,13 @@ Item {
const all = shouldShowAll(filter) const all = shouldShowAll(filter)
for (var i = 0; i < sourceModel.rowCount(); ++i) { for (var i = 0; i < sourceModelList.count; ++i) {
const publicKey = sourceModel.rowData(i, "publicKey"); let listItem = sourceModelList.itemAtIndex(i)
const item = { const item = {
alias: sourceModel.rowData(i, "alias"), publicKey: listItem.publicKey,
userName: sourceModel.rowData(i, "userName"), name: listItem.name,
publicKey: publicKey, icon: listItem.icon,
identicon: Global.getProfileImage(publicKey, false, false) || sourceModel.rowData(i, "identicon"), isIdenticon: listItem.isIdenticon
localName: sourceModel.rowData(i, "localName")
} }
if (all || isAcceptedItem(filter, item)) { if (all || isAcceptedItem(filter, item)) {
filterModel.append(item) filterModel.append(item)
@ -63,9 +79,7 @@ Item {
return return
} }
// Not Refactored Yet return globalUtils.plainText(this.filter)
return ""
// return chatsModel.plainText(this.filter)
} }
function shouldShowAll(filter) { function shouldShowAll(filter) {
@ -92,7 +106,7 @@ Item {
let filterWithoutAt = filter.substring(this.lastAtPosition + 1, this.cursorPosition) let filterWithoutAt = filter.substring(this.lastAtPosition + 1, this.cursorPosition)
filterWithoutAt = filterWithoutAt.replace(/\*/g, "") filterWithoutAt = filterWithoutAt.replace(/\*/g, "")
component.formattedFilter = filterWithoutAt suggestionsPanelRoot.formattedFilter = filterWithoutAt
return !properties.every(p => item[p].toLowerCase().match(filterWithoutAt.toLowerCase()) === null) return !properties.every(p => item[p].toLowerCase().match(filterWithoutAt.toLowerCase()) === null)
} }

View File

@ -0,0 +1,14 @@
import QtQuick 2.13
QtObject {
id: root
property var usersModule
property var usersModel
onUsersModuleChanged: {
if(!usersModule)
return
root.usersModel = usersModule.model
}
}

View File

@ -31,6 +31,12 @@ ColumnLayout {
property var chatContentModule property var chatContentModule
property var rootStore property var rootStore
property var contactsStore property var contactsStore
property UsersStore usersStore: UsersStore {}
onChatContentModuleChanged: {
chatContentRoot.usersStore.usersModule = chatContentRoot.chatContentModule.usersModule
}
property Component sendTransactionNoEnsModal property Component sendTransactionNoEnsModal
property Component receiveTransactionModal property Component receiveTransactionModal
@ -70,7 +76,7 @@ ColumnLayout {
//% "Public chat" //% "Public chat"
return qsTrId("public-chat") return qsTrId("public-chat")
case Constants.chatType.privateGroupChat: case Constants.chatType.privateGroupChat:
let cnt = chatContentModule.usersModule.model.count let cnt = chatContentRoot.usersStore.usersModule.count
//% "%1 members" //% "%1 members"
if(cnt > 1) return qsTrId("-1-members").arg(cnt); if(cnt > 1) return qsTrId("-1-members").arg(cnt);
//% "1 member" //% "1 member"
@ -344,6 +350,7 @@ ColumnLayout {
contactsStore: chatContentRoot.contactsStore contactsStore: chatContentRoot.contactsStore
messageContextMenuInst: contextmenu messageContextMenuInst: contextmenu
messageStore: messageStore messageStore: messageStore
usersStore: chatContentRoot.usersStore
stickersLoaded: chatContentRoot.stickersLoaded stickersLoaded: chatContentRoot.stickersLoaded
onShowReplyArea: { onShowReplyArea: {
let obj = messageStore.getMessageByIdAsJson(messageId) let obj = messageStore.getMessageByIdAsJson(messageId)
@ -377,6 +384,9 @@ ColumnLayout {
StatusChatInput { StatusChatInput {
id: chatInput id: chatInput
usersStore: chatContentRoot.usersStore
visible: { visible: {
// Not Refactored Yet // Not Refactored Yet
return true return true

View File

@ -23,6 +23,7 @@ Item {
property var store property var store
property var messageStore property var messageStore
property var usersStore
property var contactsStore property var contactsStore
property bool stickersLoaded: false property bool stickersLoaded: false
@ -296,6 +297,7 @@ Item {
id: msgDelegate id: msgDelegate
messageStore: root.messageStore messageStore: root.messageStore
usersStore: root.usersStore
contactsStore: root.contactsStore contactsStore: root.contactsStore
messageContextMenu: messageContextMenuInst messageContextMenu: messageContextMenuInst

View File

@ -28,6 +28,8 @@ Rectangle {
signal stickerSelected(string hashId, string packId) signal stickerSelected(string hashId, string packId)
signal sendMessage(var event) signal sendMessage(var event)
property var usersStore
property bool emojiEvent: false; property bool emojiEvent: false;
property bool paste: false; property bool paste: false;
property bool isColonPressed: false; property bool isColonPressed: false;
@ -288,9 +290,7 @@ Rectangle {
const deparsedEmoji = Emoji.deparse(textWithoutMention); const deparsedEmoji = Emoji.deparse(textWithoutMention);
// Not Refactored Yet return globalUtils.plainText(deparsedEmoji)
return ""
//return RootStore.chatsModelInst.plainText(deparsedEmoji);
} }
function removeMentions(currentText) { function removeMentions(currentText) {
@ -622,26 +622,16 @@ Rectangle {
SuggestionBoxPanel { SuggestionBoxPanel {
id: suggestionsBox id: suggestionsBox
// Not Refactored Yet model: control.usersStore.usersModel
// model: {
// if (RootStore.chatsModelInst.communities.activeCommunity.active) {
// return RootStore.chatsModelInst.communities.activeCommunity.members
// }
// return RootStore.chatsModelInst.messageView.messageList.userList
// }
x : messageInput.x x : messageInput.x
y: -height - Style.current.smallPadding y: -height - Style.current.smallPadding
width: messageInput.width width: messageInput.width
filter: messageInputField.text filter: messageInputField.text
cursorPosition: messageInputField.cursorPosition cursorPosition: messageInputField.cursorPosition
property: ["userName", "localName", "alias"] property: ["name"]
onItemSelected: function (item, lastAtPosition, lastCursorPosition) { onItemSelected: function (item, lastAtPosition, lastCursorPosition) {
const properties = "userName, alias"; // Ignore localName let name = item.name.replace("@", "")
let aliasName = item[properties.split(",").map(p => p.trim()).find(p => !!item[p])] insertMention(name, lastAtPosition, lastCursorPosition)
aliasName = aliasName.replace("@", "")
aliasName = aliasName.replace(/(\.stateofus)?\.eth/, "")
insertMention(aliasName, lastAtPosition, lastCursorPosition)
suggestionsBox.suggestionsModel.clear() suggestionsBox.suggestionsModel.clear()
} }
} }
@ -1091,8 +1081,7 @@ Rectangle {
anchors.rightMargin: Style.current.halfPadding anchors.rightMargin: Style.current.halfPadding
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: imageBtn2.visible visible: imageBtn2.visible
// Not Refactored Yet enabled: (globalUtils.plainText(Emoji.deparse(messageInputField.text)).length > 0 || isImage) && messageInputField.length < messageLimit
// enabled: (RootStore.chatsModelInst.plainText(Emoji.deparse(messageInputField.text)).length > 0 || isImage) && messageInputField.length < messageLimit
onClicked: function (event) { onClicked: function (event) {
control.sendMessage(event) control.sendMessage(event)
control.hideExtendedArea(); control.hideExtendedArea();

View File

@ -11,9 +11,7 @@ Item {
property var store property var store
property bool longChatText: true property bool longChatText: true
// Not Refactored Yet property bool veryLongChatText: globalUtils.plainText(message).length > Constants.limitLongChatTextCompactMode
property bool veryLongChatText: false // !!root.store ? root.store.chatsModelInst.plainText(message).length >
//Constants.limitLongChatTextCompactMode : false
property bool readMore: false property bool readMore: false
property alias textField: chatText property alias textField: chatText

View File

@ -15,6 +15,7 @@ Item {
id: root id: root
property var messageStore property var messageStore
property var usersStore
property var contactsStore property var contactsStore
property var messageContextMenu property var messageContextMenu
@ -364,12 +365,13 @@ Item {
if (index < 0) { if (index < 0) {
break break
} }
let endIndex = message.indexOf("</a>", index) let startIndex = index
let endIndex = message.indexOf("</a>", index) + 4
if (endIndex < 0) { if (endIndex < 0) {
index += 8 // "<a href=" index += 8 // "<a href="
continue continue
} }
let addrIndex = rmessage.indexOf("0x", index + 8) let addrIndex = message.indexOf("0x", index + 8)
if (addrIndex < 0) { if (addrIndex < 0) {
index += 8 // "<a href=" index += 8 // "<a href="
continue continue
@ -379,21 +381,19 @@ Item {
index += 8 // "<a href=" index += 8 // "<a href="
continue continue
} }
const address = '@' + message.substring(addrIndex, addrEndIndex) const mentionLink = message.substring(startIndex, endIndex)
const linkTag = message.substring(index, endIndex + 5) const linkTag = message.substring(index, endIndex)
const linkText = linkTag.replace(/(<([^>]+)>)/ig,"").trim() const linkText = linkTag.replace(/(<([^>]+)>)/ig,"").trim()
const atSymbol = linkText.startsWith("@") ? '' : '@' const atSymbol = linkText.startsWith("@") ? '' : '@'
const mentionTag = Constants.mentionSpanTag + atSymbol + linkText + '</span> ' const mentionTag = Constants.mentionSpanTag + atSymbol + linkText + '</span> '
mentionsMap.set(address, mentionTag) mentionsMap.set(mentionLink, mentionTag)
index += linkTag.length index += linkTag.length
} }
sourceText = rootStore.plainText(Emoji.deparse(message)) sourceText = message
for (let [key, value] of mentionsMap) { for (let [key, value] of mentionsMap) {
sourceText = sourceText.replace(new RegExp(key, 'g'), value) sourceText = sourceText.replace(new RegExp(key, 'g'), value)
} }
sourceText = sourceText.replace(/\n/g, "<br />")
sourceText = Utils.getMessageWithStyle(sourceText, isCurrentUser)
} }
sourceComponent: Item { sourceComponent: Item {
@ -410,6 +410,9 @@ Item {
StatusChatInput { StatusChatInput {
id: editTextInput id: editTextInput
usersStore: root.usersStore
chatInputPlaceholder: qsTrId("type-a-message-") chatInputPlaceholder: qsTrId("type-a-message-")
chatType: messageStore.getChatType() chatType: messageStore.getChatType()
isEdit: true isEdit: true

View File

@ -18,6 +18,7 @@ Column {
z: (typeof chatLogView === "undefined") ? 1 : (chatLogView.count - index) z: (typeof chatLogView === "undefined") ? 1 : (chatLogView.count - index)
property var messageStore property var messageStore
property var usersStore
property var contactsStore property var contactsStore
property var messageContextMenu property var messageContextMenu
@ -342,6 +343,7 @@ Column {
CompactMessageView { CompactMessageView {
messageStore: root.messageStore messageStore: root.messageStore
usersStore: root.usersStore
contactsStore: root.contactsStore contactsStore: root.contactsStore
messageContextMenu: root.messageContextMenu messageContextMenu: root.messageContextMenu
contentType: root.messageContentType contentType: root.messageContentType