From aa6036b78bc3999f77146d082dd61a2655bba933 Mon Sep 17 00:00:00 2001 From: Sale Djenic Date: Tue, 8 Feb 2022 13:08:02 +0100 Subject: [PATCH] fix(@desktop/chat): mention suggestion list fixed --- src/app_service/common/message.nim | 35 ++++++++++++++++ src/app_service/service/chat/service.nim | 6 ++- src/app_service/service/message/service.nim | 9 ++++- .../Chat/panels/SuggestionBoxPanel.qml | 6 +-- .../Chat/panels/SuggestionFilterPanel.qml | 40 +++++++++++++------ ui/app/AppLayouts/Chat/stores/UsersStore.qml | 14 +++++++ .../AppLayouts/Chat/views/ChatContentView.qml | 12 +++++- .../Chat/views/ChatMessagesView.qml | 2 + ui/imports/shared/status/StatusChatInput.qml | 27 ++++--------- ui/imports/shared/views/chat/ChatTextView.qml | 4 +- .../shared/views/chat/CompactMessageView.qml | 19 +++++---- ui/imports/shared/views/chat/MessageView.qml | 2 + 12 files changed, 126 insertions(+), 50 deletions(-) create mode 100644 src/app_service/common/message.nim create mode 100644 ui/app/AppLayouts/Chat/stores/UsersStore.qml diff --git a/src/app_service/common/message.nim b/src/app_service/common/message.nim new file mode 100644 index 000000000..5cf1c2874 --- /dev/null +++ b/src/app_service/common/message.nim @@ -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 \ No newline at end of file diff --git a/src/app_service/service/chat/service.nim b/src/app_service/service/chat/service.nim index 8e36d25e7..01c7ed85d 100644 --- a/src/app_service/service/chat/service.nim +++ b/src/app_service/service/chat/service.nim @@ -10,6 +10,7 @@ import ../../../app/global/global_singleton import ../../../app/core/eventemitter import ../../../constants +import ../../common/message as message_common from ../../common/account_constants import ZERO_ADDRESS export chat_dto @@ -271,9 +272,12 @@ QtObject: preferredUsername: string = "", communityId: string = "") = try: + let allKnownContacts = self.contactService.getContacts() + let processedMsg = message_common.replaceMentionsWithPubKeys(allKnownContacts, msg) + let response = status_chat.sendChatMessage( chatId, - msg, + processedMsg, replyTo, contentType, preferredUsername, diff --git a/src/app_service/service/message/service.nim b/src/app_service/service/message/service.nim index 868d07af5..abb1c78a0 100644 --- a/src/app_service/service/message/service.nim +++ b/src/app_service/service/message/service.nim @@ -13,6 +13,8 @@ import ../chat/dto/chat as chat_dto import ./dto/pinned_message_update as pinned_msg_update_dto import ./dto/removed_message as removed_msg_dto +import ../../common/message as message_common + export message_dto export pinned_msg_dto export reaction_dto @@ -652,9 +654,12 @@ proc deleteMessage*(self: Service, messageId: string) = except Exception as e: 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: - 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 messages: seq[MessageDto] diff --git a/ui/app/AppLayouts/Chat/panels/SuggestionBoxPanel.qml b/ui/app/AppLayouts/Chat/panels/SuggestionBoxPanel.qml index 1f6cf1e47..f43285690 100644 --- a/ui/app/AppLayouts/Chat/panels/SuggestionBoxPanel.qml +++ b/ui/app/AppLayouts/Chat/panels/SuggestionBoxPanel.qml @@ -33,7 +33,7 @@ import shared.panels 1.0 Rectangle { id: container - property QtObject model + property var model property Item delegate property alias suggestionsModel: filterItem.model property alias filter: filterItem.filter @@ -193,8 +193,8 @@ Rectangle { anchors.leftMargin: Style.current.smallPadding image.width: 32 image.height: 32 - image.source: model.identicon - image.isIdenticon: true + image.source: model.icon + image.isIdenticon: model.isIdenticon } StyledText { diff --git a/ui/app/AppLayouts/Chat/panels/SuggestionFilterPanel.qml b/ui/app/AppLayouts/Chat/panels/SuggestionFilterPanel.qml index ff188b81d..aa29f0868 100644 --- a/ui/app/AppLayouts/Chat/panels/SuggestionFilterPanel.qml +++ b/ui/app/AppLayouts/Chat/panels/SuggestionFilterPanel.qml @@ -2,11 +2,11 @@ import QtQuick 2.13 import utils 1.0 Item { - id: component + id: suggestionsPanelRoot property alias model: filterModel property string formattedFilter - property QtObject sourceModel: undefined + property var sourceModel property string filter: "" property int cursorPosition: 0 property int lastAtPosition: 0 @@ -17,6 +17,23 @@ Item { onSourceModelChanged: 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 { id: filterModel } @@ -43,14 +60,13 @@ Item { const all = shouldShowAll(filter) - for (var i = 0; i < sourceModel.rowCount(); ++i) { - const publicKey = sourceModel.rowData(i, "publicKey"); + for (var i = 0; i < sourceModelList.count; ++i) { + let listItem = sourceModelList.itemAtIndex(i) const item = { - alias: sourceModel.rowData(i, "alias"), - userName: sourceModel.rowData(i, "userName"), - publicKey: publicKey, - identicon: Global.getProfileImage(publicKey, false, false) || sourceModel.rowData(i, "identicon"), - localName: sourceModel.rowData(i, "localName") + publicKey: listItem.publicKey, + name: listItem.name, + icon: listItem.icon, + isIdenticon: listItem.isIdenticon } if (all || isAcceptedItem(filter, item)) { filterModel.append(item) @@ -63,9 +79,7 @@ Item { return } - // Not Refactored Yet - return "" -// return chatsModel.plainText(this.filter) + return globalUtils.plainText(this.filter) } function shouldShowAll(filter) { @@ -92,7 +106,7 @@ Item { let filterWithoutAt = filter.substring(this.lastAtPosition + 1, this.cursorPosition) filterWithoutAt = filterWithoutAt.replace(/\*/g, "") - component.formattedFilter = filterWithoutAt + suggestionsPanelRoot.formattedFilter = filterWithoutAt return !properties.every(p => item[p].toLowerCase().match(filterWithoutAt.toLowerCase()) === null) } diff --git a/ui/app/AppLayouts/Chat/stores/UsersStore.qml b/ui/app/AppLayouts/Chat/stores/UsersStore.qml new file mode 100644 index 000000000..cbc6e8ae2 --- /dev/null +++ b/ui/app/AppLayouts/Chat/stores/UsersStore.qml @@ -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 + } +} diff --git a/ui/app/AppLayouts/Chat/views/ChatContentView.qml b/ui/app/AppLayouts/Chat/views/ChatContentView.qml index b96bf555f..61f3fc14a 100644 --- a/ui/app/AppLayouts/Chat/views/ChatContentView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatContentView.qml @@ -31,6 +31,12 @@ ColumnLayout { property var chatContentModule property var rootStore property var contactsStore + property UsersStore usersStore: UsersStore {} + + onChatContentModuleChanged: { + chatContentRoot.usersStore.usersModule = chatContentRoot.chatContentModule.usersModule + } + property Component sendTransactionNoEnsModal property Component receiveTransactionModal @@ -70,7 +76,7 @@ ColumnLayout { //% "Public chat" return qsTrId("public-chat") case Constants.chatType.privateGroupChat: - let cnt = chatContentModule.usersModule.model.count + let cnt = chatContentRoot.usersStore.usersModule.count //% "%1 members" if(cnt > 1) return qsTrId("-1-members").arg(cnt); //% "1 member" @@ -344,6 +350,7 @@ ColumnLayout { contactsStore: chatContentRoot.contactsStore messageContextMenuInst: contextmenu messageStore: messageStore + usersStore: chatContentRoot.usersStore stickersLoaded: chatContentRoot.stickersLoaded onShowReplyArea: { let obj = messageStore.getMessageByIdAsJson(messageId) @@ -377,6 +384,9 @@ ColumnLayout { StatusChatInput { id: chatInput + + usersStore: chatContentRoot.usersStore + visible: { // Not Refactored Yet return true diff --git a/ui/app/AppLayouts/Chat/views/ChatMessagesView.qml b/ui/app/AppLayouts/Chat/views/ChatMessagesView.qml index 030896821..d7d3d4710 100644 --- a/ui/app/AppLayouts/Chat/views/ChatMessagesView.qml +++ b/ui/app/AppLayouts/Chat/views/ChatMessagesView.qml @@ -23,6 +23,7 @@ Item { property var store property var messageStore + property var usersStore property var contactsStore property bool stickersLoaded: false @@ -296,6 +297,7 @@ Item { id: msgDelegate messageStore: root.messageStore + usersStore: root.usersStore contactsStore: root.contactsStore messageContextMenu: messageContextMenuInst diff --git a/ui/imports/shared/status/StatusChatInput.qml b/ui/imports/shared/status/StatusChatInput.qml index 8134b0ef1..181aff36f 100644 --- a/ui/imports/shared/status/StatusChatInput.qml +++ b/ui/imports/shared/status/StatusChatInput.qml @@ -28,6 +28,8 @@ Rectangle { signal stickerSelected(string hashId, string packId) signal sendMessage(var event) + property var usersStore + property bool emojiEvent: false; property bool paste: false; property bool isColonPressed: false; @@ -288,9 +290,7 @@ Rectangle { const deparsedEmoji = Emoji.deparse(textWithoutMention); - // Not Refactored Yet - return "" - //return RootStore.chatsModelInst.plainText(deparsedEmoji); + return globalUtils.plainText(deparsedEmoji) } function removeMentions(currentText) { @@ -622,26 +622,16 @@ Rectangle { SuggestionBoxPanel { id: suggestionsBox - // Not Refactored Yet -// model: { -// if (RootStore.chatsModelInst.communities.activeCommunity.active) { -// return RootStore.chatsModelInst.communities.activeCommunity.members -// } -// return RootStore.chatsModelInst.messageView.messageList.userList -// } + model: control.usersStore.usersModel x : messageInput.x y: -height - Style.current.smallPadding width: messageInput.width filter: messageInputField.text cursorPosition: messageInputField.cursorPosition - property: ["userName", "localName", "alias"] + property: ["name"] onItemSelected: function (item, lastAtPosition, lastCursorPosition) { - const properties = "userName, alias"; // Ignore localName - let aliasName = item[properties.split(",").map(p => p.trim()).find(p => !!item[p])] - aliasName = aliasName.replace("@", "") - aliasName = aliasName.replace(/(\.stateofus)?\.eth/, "") - - insertMention(aliasName, lastAtPosition, lastCursorPosition) + let name = item.name.replace("@", "") + insertMention(name, lastAtPosition, lastCursorPosition) suggestionsBox.suggestionsModel.clear() } } @@ -1091,8 +1081,7 @@ Rectangle { anchors.rightMargin: Style.current.halfPadding anchors.verticalCenter: parent.verticalCenter visible: imageBtn2.visible - // Not Refactored Yet -// enabled: (RootStore.chatsModelInst.plainText(Emoji.deparse(messageInputField.text)).length > 0 || isImage) && messageInputField.length < messageLimit + enabled: (globalUtils.plainText(Emoji.deparse(messageInputField.text)).length > 0 || isImage) && messageInputField.length < messageLimit onClicked: function (event) { control.sendMessage(event) control.hideExtendedArea(); diff --git a/ui/imports/shared/views/chat/ChatTextView.qml b/ui/imports/shared/views/chat/ChatTextView.qml index 4a901fa0e..62e3cf2d0 100644 --- a/ui/imports/shared/views/chat/ChatTextView.qml +++ b/ui/imports/shared/views/chat/ChatTextView.qml @@ -11,9 +11,7 @@ Item { property var store property bool longChatText: true - // Not Refactored Yet - property bool veryLongChatText: false // !!root.store ? root.store.chatsModelInst.plainText(message).length > - //Constants.limitLongChatTextCompactMode : false + property bool veryLongChatText: globalUtils.plainText(message).length > Constants.limitLongChatTextCompactMode property bool readMore: false property alias textField: chatText diff --git a/ui/imports/shared/views/chat/CompactMessageView.qml b/ui/imports/shared/views/chat/CompactMessageView.qml index d4738886a..51646ce4d 100644 --- a/ui/imports/shared/views/chat/CompactMessageView.qml +++ b/ui/imports/shared/views/chat/CompactMessageView.qml @@ -15,6 +15,7 @@ Item { id: root property var messageStore + property var usersStore property var contactsStore property var messageContextMenu @@ -364,12 +365,13 @@ Item { if (index < 0) { break } - let endIndex = message.indexOf("", index) + let startIndex = index + let endIndex = message.indexOf("", index) + 4 if (endIndex < 0) { index += 8 // " ' - mentionsMap.set(address, mentionTag) + mentionsMap.set(mentionLink, mentionTag) index += linkTag.length } - sourceText = rootStore.plainText(Emoji.deparse(message)) + sourceText = message for (let [key, value] of mentionsMap) { sourceText = sourceText.replace(new RegExp(key, 'g'), value) } - sourceText = sourceText.replace(/\n/g, "
") - sourceText = Utils.getMessageWithStyle(sourceText, isCurrentUser) } sourceComponent: Item { @@ -410,6 +410,9 @@ Item { StatusChatInput { id: editTextInput + + usersStore: root.usersStore + chatInputPlaceholder: qsTrId("type-a-message-") chatType: messageStore.getChatType() isEdit: true diff --git a/ui/imports/shared/views/chat/MessageView.qml b/ui/imports/shared/views/chat/MessageView.qml index 0b92693f4..b1ab4f635 100644 --- a/ui/imports/shared/views/chat/MessageView.qml +++ b/ui/imports/shared/views/chat/MessageView.qml @@ -18,6 +18,7 @@ Column { z: (typeof chatLogView === "undefined") ? 1 : (chatLogView.count - index) property var messageStore + property var usersStore property var contactsStore property var messageContextMenu @@ -342,6 +343,7 @@ Column { CompactMessageView { messageStore: root.messageStore + usersStore: root.usersStore contactsStore: root.contactsStore messageContextMenu: root.messageContextMenu contentType: root.messageContentType