diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index 722a4c2b30..803a2301b8 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -752,7 +752,7 @@ method getVerificationRequestFrom*[T](self: Module[T], publicKey: string): Verif self.controller.getVerificationRequestFrom(publicKey) method getContactDetailsAsJson*[T](self: Module[T], publicKey: string): string = - let contact = self.controller.getContact(publicKey) + let contact = self.controller.getContact(publicKey) let (name, _, _) = self.controller.getContactNameAndImage(contact.id) let request = self.getVerificationRequestFrom(publicKey) let jsonObj = %* { diff --git a/src/app_service/service/contacts/service.nim b/src/app_service/service/contacts/service.nim index 7b86f8a974..3eeb14dd4e 100644 --- a/src/app_service/service/contacts/service.nim +++ b/src/app_service/service/contacts/service.nim @@ -5,6 +5,7 @@ import ../../../app/core/signals/types import ../../../app/core/eventemitter import ../../../app/core/tasks/[qt, threadpool] import ../../common/types as common_types +import ../../common/conversion as service_conversion import ../settings/service as settings_service import ../network/service as network_service @@ -289,7 +290,16 @@ QtObject: return TrustStatus.Unknown proc getContactById*(self: Service, id: string): ContactsDto = - if(id == singletonInstance.userProfile.getPubKey()): + + var pubkey = id + + if len(pubkey) == 0: + return + + if service_conversion.isCompressedPubKey(id): + pubkey = status_accounts.decompressPk(id).result + + if(pubkey == singletonInstance.userProfile.getPubKey()): # If we try to get the contact details of ourselves, just return our own info return ContactsDto( id: singletonInstance.userProfile.getPubKey(), @@ -309,25 +319,25 @@ QtObject: ## Returns contact details based on passed id (public key) ## If we don't have stored contact localy or in the db then we create it based on public key. - if(self.contacts.hasKey(id)): - return self.contacts[id] + if(self.contacts.hasKey(pubkey)): + return self.contacts[pubkey] - result = self.fetchContact(id) + result = self.fetchContact(pubkey) if result.id.len == 0: - if(not id.startsWith("0x")): + if(not pubkey.startsWith("0x")): debug "id is not in a hex format" return var num64: int64 - let parsedChars = parseHex(id, num64) + let parsedChars = parseHex(pubkey, num64) if(parsedChars != PK_LENGTH_0X_INCLUDED): debug "id doesn't have expected lenght" return - let alias = self.generateAlias(id) - let trustStatus = self.getTrustStatus(id) + let alias = self.generateAlias(pubkey) + let trustStatus = self.getTrustStatus(pubkey) result = ContactsDto( - id: id, + id: pubkey, alias: alias, ensVerified: false, added: false, diff --git a/ui/app/AppLayouts/Chat/panels/InlineSelectorPanel.qml b/ui/app/AppLayouts/Chat/panels/InlineSelectorPanel.qml index df535b37ae..de5fefb71b 100644 --- a/ui/app/AppLayouts/Chat/panels/InlineSelectorPanel.qml +++ b/ui/app/AppLayouts/Chat/panels/InlineSelectorPanel.qml @@ -29,6 +29,7 @@ Item { signal entryAccepted(var suggestionsDelegate) signal entryRemoved(var delegate) + signal textPasted(string text) implicitWidth: mainLayout.implicitWidth implicitHeight: mainLayout.implicitHeight @@ -89,6 +90,11 @@ Item { verticalAlignment: Text.AlignVCenter font.pixelSize: 15 color: Theme.palette.directColor1 + + selectByMouse: true + selectionColor: Theme.palette.primaryColor2 + selectedTextColor: color + cursorDelegate: Rectangle { color: Theme.palette.primaryColor1 implicitWidth: 2 @@ -103,6 +109,16 @@ Item { } Keys.onPressed: { + if (event.matches(StandardKey.Paste)) { + event.accepted = true + const previousText = text; + const previousSelectedText = selectedText; + paste() + if (previousText === "" || previousSelectedText.length === previousText.length) + root.textPasted(text) + return; + } + if (suggestionsDialog.visible) { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { root.entryAccepted(suggestionsListView.itemAtIndex(suggestionsListView.currentIndex)) diff --git a/ui/app/AppLayouts/Chat/views/CreateChatView.qml b/ui/app/AppLayouts/Chat/views/CreateChatView.qml index 204bd2c946..6920a86b6a 100644 --- a/ui/app/AppLayouts/Chat/views/CreateChatView.qml +++ b/ui/app/AppLayouts/Chat/views/CreateChatView.qml @@ -41,7 +41,6 @@ Page { Layout.leftMargin: Style.current.halfPadding Layout.rightMargin: Style.current.halfPadding rootStore: root.rootStore - enabled: root.rootStore.contactsModel.count > 0 function createChat() { if (model.count === 0) { @@ -78,7 +77,10 @@ Page { Global.closeCreateChatView() } - onVisibleChanged: if (visible) edit.forceActiveFocus() + onVisibleChanged: { + if (visible) + edit.forceActiveFocus() + } } StatusActivityCenterButton { @@ -110,6 +112,7 @@ Page { StatusListView { id: contactsList + Layout.fillWidth: true Layout.fillHeight: true visible: membersSelector.suggestionsModel.count && @@ -117,6 +120,7 @@ Page { implicitWidth: contentItem.childrenRect.width model: membersSelector.suggestionsModel delegate: ContactListItemDelegate { + width: ListView.view.width onClicked: membersSelector.entryAccepted(this) } } diff --git a/ui/app/AppLayouts/Chat/views/MembersSelectorView.qml b/ui/app/AppLayouts/Chat/views/MembersSelectorView.qml index 3fd4c5167d..8519289720 100644 --- a/ui/app/AppLayouts/Chat/views/MembersSelectorView.qml +++ b/ui/app/AppLayouts/Chat/views/MembersSelectorView.qml @@ -7,6 +7,8 @@ import StatusQ.Components 0.1 import "private" +import utils 1.0 + import SortFilterProxyModel 0.2 MembersSelectorBase { @@ -20,16 +22,20 @@ MembersSelectorBase { } onEntryAccepted: { - if (!root.limitReached) { - d.addMember(suggestionsDelegate._pubKey, suggestionsDelegate.userName) + if (root.limitReached) + return + if (d.addMember(suggestionsDelegate._pubKey, suggestionsDelegate.userName)) root.edit.clear() - } } onEntryRemoved: { d.removeMember(delegate._pubKey) } + onTextPasted: { + d.lookupContact(text); + } + model: SortFilterProxyModel { sourceModel: d.selectedMembers } @@ -47,12 +53,25 @@ MembersSelectorBase { property ListModel selectedMembers: ListModel {} + function lookupContact(value) { + if (value.startsWith(Constants.userLinkPrefix)) + value = value.slice(Constants.userLinkPrefix.length) + root.rootStore.contactsStore.resolveENS(value) + } + function addMember(pubKey, displayName) { + for (let i = 0; i < d.selectedMembers.count; ++i) { + if (d.selectedMembers.get(i).pubKey === pubKey) + return false + } + d.selectedMembers.append({ "pubKey": pubKey, "displayName": displayName }) + return true } + function removeMember(pubKey) { for(var i = 0; i < d.selectedMembers.count; i++) { const obj = d.selectedMembers.get(i) @@ -63,4 +82,34 @@ MembersSelectorBase { } } } + + Connections { + enabled: root.visible + target: root.rootStore.contactsStore.mainModuleInst + onResolvedENS: { + + if (resolvedPubKey === "") + return + + const contactDetails = Utils.getContactDetailsAsJson(resolvedPubKey) + + if (contactDetails.publicKey === root.rootStore.contactsStore.myPublicKey) + return; + + if (contactDetails.isBlocked) + return; + + if (contactDetails.isContact) { + if (d.addMember(contactDetails.publicKey, contactDetails.displayName)) + root.cleanup() + return + } + + if (root.model.count === 0) { + const popup = Global.openContactRequestPopup(contactDetails.publicKey) + popup.closed.connect(root.rejected) + return + } + } + } } diff --git a/ui/imports/shared/popups/ProfilePopup.qml b/ui/imports/shared/popups/ProfilePopup.qml index a9dd2b0321..bc903d1a83 100644 --- a/ui/imports/shared/popups/ProfilePopup.qml +++ b/ui/imports/shared/popups/ProfilePopup.qml @@ -124,7 +124,7 @@ StatusDialog { if (state === Constants.profilePopupStates.openNickname) { profileView.nicknamePopup.open(); } else if (state === Constants.profilePopupStates.contactRequest) { - sendContactRequestModal.open() + d.openContactRequestPopup() } else if (state === Constants.profilePopupStates.blockUser) { blockUser(); } else if (state === Constants.profilePopupStates.unblockUser) { @@ -168,6 +168,15 @@ StatusDialog { } } + QtObject { + id: d + + function openContactRequestPopup() { + let contactRequestPopup = Global.openContactRequestPopup(popup.userPublicKey) + contactRequestPopup.closed.connect(popup.close) + } + } + width: 700 padding: 8 @@ -238,7 +247,7 @@ StatusDialog { StatusButton { text: qsTr("Send Contact Request") visible: !userIsBlocked && !isAddedContact - onClicked: sendContactRequestModal.open() + onClicked: d.openContactRequestPopup() } StatusButton { @@ -451,21 +460,6 @@ StatusDialog { } } - // TODO: replace with StatusStackModal - SendContactRequestModal { - id: sendContactRequestModal - anchors.centerIn: parent - width: popup.width - visible: false - header.title: qsTr("Send Contact Request to %1").arg(userDisplayName) - userPublicKey: popup.userPublicKey - userDisplayName: popup.userDisplayName - userIcon: popup.userIcon - userIsEnsVerified: popup.userIsEnsVerified - onAccepted: popup.contactsStore.sendContactRequest(userPublicKey, message) - onClosed: popup.close() - } - Component { id: contactVerificationRequestPopupComponent ContactVerificationRequestPopup { diff --git a/ui/imports/shared/popups/SendContactRequestModal.qml b/ui/imports/shared/popups/SendContactRequestModal.qml index a1ea4a569c..dbd93b86ee 100644 --- a/ui/imports/shared/popups/SendContactRequestModal.qml +++ b/ui/imports/shared/popups/SendContactRequestModal.qml @@ -22,6 +22,7 @@ StatusModal { signal accepted(string message) padding: 16 + header.title: qsTr("Send Contact Request to %1").arg(userDisplayName) QtObject { id: d diff --git a/ui/imports/shared/popups/qmldir b/ui/imports/shared/popups/qmldir index 470ea5bd27..89e823b52e 100644 --- a/ui/imports/shared/popups/qmldir +++ b/ui/imports/shared/popups/qmldir @@ -22,3 +22,4 @@ ImageCropWorkflow 1.0 ImageCropWorkflow.qml ImportCommunityPopup 1.0 ImportCommunityPopup.qml DisplayNamePopup 1.0 DisplayNamePopup.qml AddEditSavedAddressPopup 1.0 AddEditSavedAddressPopup.qml +SendContactRequestModal 1.0 SendContactRequestModal.qml diff --git a/ui/imports/utils/Global.qml b/ui/imports/utils/Global.qml index 3a407ef452..836e7ee142 100644 --- a/ui/imports/utils/Global.qml +++ b/ui/imports/utils/Global.qml @@ -3,7 +3,9 @@ pragma Singleton import QtQuick 2.13 import AppLayouts.Chat.popups 1.0 -QtObject { +import shared.popups 1.0 + +Item { id: root property var applicationWindow @@ -41,6 +43,16 @@ QtObject { signal openEditDisplayNamePopup() signal openActivityCenterPopupRequested + function openContactRequestPopup(publicKey) { + const contactDetails = Utils.getContactDetailsAsJson(publicKey); + return openPopup(sendContactRequestPopupComponent, { + userPublicKey: publicKey, + userDisplayName: contactDetails.displayName, + userIcon: contactDetails.largeImage, + userIsEnsVerified: contactDetails.ensVerified, + }) + } + function openProfilePopup(publicKey, parentPopup, state = "") { openProfilePopupRequested(publicKey, parentPopup, state); } @@ -113,4 +125,13 @@ QtObject { function settingsHasLoaded() { settingsLoaded() } + + Component { + id: sendContactRequestPopupComponent + SendContactRequestModal { + anchors.centerIn: parent + onAccepted: appMain.rootStore.profileSectionStore.contactsStore.sendContactRequest(userPublicKey, message) + onClosed: destroy() + } + } }