diff --git a/ui/app/AppLayouts/Profile/panels/ContactPanel.qml b/ui/app/AppLayouts/Profile/panels/ContactPanel.qml index 4a1d040c50..3d0139d927 100644 --- a/ui/app/AppLayouts/Profile/panels/ContactPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ContactPanel.qml @@ -23,6 +23,8 @@ StatusListItem { height: visible ? implicitHeight : 0 title: container.name + property var contactsStore + property string name: "Jotaro Kujo" property string publicKey: "0x04d8c07dd137bd1b73a6f51df148b4f77ddaa11209d36e43d8344c0a7d6db1cad6085f27cfb75dd3ae21d86ceffebe4cf8a35b9ce8d26baa19dc264efe6d8f221b" property string iconSource: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=" @@ -42,8 +44,7 @@ StatusListItem { property string contactText: "" property bool contactTextClickable: false - signal openProfilePopup(string publicKey) - signal openChangeNicknamePopup(string publicKey) + signal openContactContextMenu(string publicKey, string name, string icon) signal sendMessageActionTriggered(string publicKey) signal showVerificationRequest(string publicKey) signal contactRequestAccepted(string publicKey) @@ -139,61 +140,7 @@ StatusListItem { icon.name: "more" icon.color: Theme.palette.directColor1 onClicked: { - highlighted = true - contactContextMenu.popup(-contactContextMenu.width+menuButton.width, menuButton.height + 4) - } - - StatusPopupMenu { - id: contactContextMenu - - onClosed: { - menuButton.highlighted = false - } - - ProfileHeader { - width: parent.width - - displayName: container.name - pubkey: container.publicKey - icon: container.iconSource - } - - Item { - height: 8 - } - - Separator {} - - ViewProfileMenuItem { - icon.width: 16 - icon.height: 16 - onTriggered: { - container.openProfilePopup(container.publicKey) - menuButton.highlighted = false - } - } - - SendMessageMenuItem { - icon.width: 16 - icon.height: 16 - onTriggered: { - container.sendMessageActionTriggered(container.publicKey) - menuButton.highlighted = false - } - enabled: container.isContact - } - - StatusMenuItem { - text: qsTr("Rename") - icon.name: "edit_pencil" - icon.width: 16 - icon.height: 16 - onTriggered: { - container.openChangeNicknamePopup(container.publicKey) - menuButton.highlighted = false - } - enabled: !container.isBlocked - } + container.openContactContextMenu(container.publicKey, container.name, container.icon) } } ] diff --git a/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml b/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml index e00a5a5bcd..81c10ab457 100644 --- a/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ContactsListPanel.qml @@ -16,6 +16,7 @@ import "." Item { id: contactListRoot + property var contactsStore property var contactsModel property int panelUsage: Constants.contactsPanelUsage.unknownPosition property bool scrollbarOn: false @@ -26,11 +27,10 @@ Item { readonly property string lowerCaseSearchString: searchString.toLowerCase() readonly property int count: contactsList.count + signal openContactContextMenu(string publicKey, string name, string icon) signal contactClicked(string publicKey) - signal openProfilePopup(string publicKey) signal sendMessageActionTriggered(string publicKey) signal showVerificationRequest(string publicKey) - signal openChangeNicknamePopup(string publicKey) signal contactRequestAccepted(string publicKey) signal contactRequestRejected(string publicKey) signal rejectionRemoved(string publicKey) @@ -108,6 +108,7 @@ Item { ContactPanel { id: panelDelegate width: ListView.view.width + contactsStore: contactListRoot.contactsStore name: model.displayName publicKey: model.pubKey iconSource: model.icon @@ -120,6 +121,9 @@ Item { searchStr: contactListRoot.searchString showSendMessageButton: isContact && !isBlocked + onOpenContactContextMenu: function (publicKey, name, icon) { + contactListRoot.openContactContextMenu(publicKey, name, icon) + } showRejectContactRequestButton: { if (contactListRoot.panelUsage === Constants.contactsPanelUsage.receivedContactRequest && !model.verificationRequestStatus) { return true @@ -156,9 +160,7 @@ Item { } onClicked: contactListRoot.contactClicked(model.pubKey) - onOpenProfilePopup: contactListRoot.openProfilePopup(publicKey) onSendMessageActionTriggered: contactListRoot.sendMessageActionTriggered(publicKey) - onOpenChangeNicknamePopup: contactListRoot.openChangeNicknamePopup(publicKey) onContactRequestAccepted: contactListRoot.contactRequestAccepted(publicKey) onContactRequestRejected: contactListRoot.contactRequestRejected(publicKey) onRejectionRemoved: contactListRoot.rejectionRemoved(publicKey) diff --git a/ui/app/AppLayouts/Profile/views/ContactsView.qml b/ui/app/AppLayouts/Profile/views/ContactsView.qml index 2664e61ba7..c3a89536f3 100644 --- a/ui/app/AppLayouts/Profile/views/ContactsView.qml +++ b/ui/app/AppLayouts/Profile/views/ContactsView.qml @@ -13,6 +13,7 @@ import shared.views 1.0 import shared.panels 1.0 import shared.popups 1.0 import shared.controls 1.0 +import shared.views.chat 1.0 import "../stores" import "../panels" @@ -36,9 +37,31 @@ SettingsContentBase { } ] + + function openContextMenu(publicKey, name, icon) { + contactContextMenu.selectedUserPublicKey = publicKey + contactContextMenu.selectedUserDisplayName = name + contactContextMenu.selectedUserIcon = icon + contactContextMenu.popup() + } + Item { id: contentItem + MessageContextMenuView { + id: contactContextMenu + store: ({contactsStore: root.contactsStore}) + isProfile: true + + onOpenProfileClicked: function (pubkey, state) { + Global.openProfilePopup(pubkey, null, state) + } + + onCreateOneToOneChat: function (communityId, chatId, ensName) { + root.contactsStore.joinPrivateChat(chatId) + } + } + SearchBox { id: searchBox anchors.left: parent.left @@ -101,17 +124,14 @@ SettingsContentBase { title: qsTr("Identity Verified Contacts") contactsModel: root.contactsStore.myContactsModel searchString: searchBox.text - panelUsage: Constants.contactsPanelUsage.verifiedMutualContacts - onOpenProfilePopup: { - Global.openProfilePopup(publicKey) + onOpenContactContextMenu: function (publicKey, name, icon) { + root.openContextMenu(publicKey, name, icon) } + contactsStore: root.contactsStore + panelUsage: Constants.contactsPanelUsage.verifiedMutualContacts onSendMessageActionTriggered: { root.contactsStore.joinPrivateChat(publicKey) } - - onOpenChangeNicknamePopup: { - Global.openProfilePopup(publicKey, null, "openNickname") - } } ContactsListPanel { @@ -123,18 +143,15 @@ SettingsContentBase { title: qsTr("Contacts") contactsModel: root.contactsStore.myContactsModel searchString: searchBox.text - panelUsage: Constants.contactsPanelUsage.mutualContacts - onOpenProfilePopup: { - Global.openProfilePopup(publicKey) + contactsStore: root.contactsStore + onOpenContactContextMenu: function (publicKey, name, icon) { + root.openContextMenu(publicKey, name, icon) } + panelUsage: Constants.contactsPanelUsage.mutualContacts onSendMessageActionTriggered: { root.contactsStore.joinPrivateChat(publicKey) } - - onOpenChangeNicknamePopup: { - Global.openProfilePopup(publicKey, null, "openNickname") - } } Item { width: parent.width @@ -159,17 +176,13 @@ SettingsContentBase { (contactsListHeight > (stackLayout.height - sentRequests.contactsListHeight)) title: qsTr("Received") searchString: searchBox.text + contactsStore: root.contactsStore + onOpenContactContextMenu: function (publicKey, name, icon) { + root.openContextMenu(publicKey, name, icon) + } contactsModel: root.contactsStore.receivedContactRequestsModel panelUsage: Constants.contactsPanelUsage.receivedContactRequest - onOpenProfilePopup: { - Global.openProfilePopup(publicKey) - } - - onOpenChangeNicknamePopup: { - Global.openProfilePopup(publicKey, null, "openNickname") - } - onContactRequestAccepted: { root.contactsStore.acceptContactRequest(publicKey) } @@ -205,16 +218,12 @@ SettingsContentBase { (contactsListHeight > (stackLayout.height - receivedRequests.contactsListHeight)) title: qsTr("Sent") searchString: searchBox.text + contactsStore: root.contactsStore + onOpenContactContextMenu: function (publicKey, name, icon) { + root.openContextMenu(publicKey, name, icon) + } contactsModel: root.contactsStore.sentContactRequestsModel panelUsage: Constants.contactsPanelUsage.sentContactRequest - - onOpenProfilePopup: { - Global.openProfilePopup(publicKey) - } - - onOpenChangeNicknamePopup: { - Global.openProfilePopup(publicKey, null, "openNickname") - } } } @@ -233,17 +242,13 @@ SettingsContentBase { // clip: true // title: qsTr("Received") // searchString: searchBox.text + // contactsStore: root.contactsStore + // onOpenContactContextMenu: function (publicKey, name, icon) { + // root.openContextMenu(publicKey, name, icon) + // } // contactsModel: root.contactsStore.receivedButRejectedContactRequestsModel // panelUsage: Constants.contactsPanelUsage.rejectedReceivedContactRequest - // onOpenProfilePopup: { - // Global.openProfilePopup(publicKey) - // } - - // onOpenChangeNicknamePopup: { - // Global.openProfilePopup(publicKey, null, true) - // } - // onRejectionRemoved: { // root.contactsStore.removeContactRequestRejection(publicKey) // } @@ -255,16 +260,12 @@ SettingsContentBase { // clip: true // title: qsTr("Sent") // searchString: searchBox.text + // contactsStore: root.contactsStore + // onOpenContactContextMenu: function (publicKey, name, icon) { + // root.openContextMenu(publicKey, name, icon) + // } // contactsModel: root.contactsStore.sentButRejectedContactRequestsModel // panelUsage: Constants.contactsPanelUsage.rejectedSentContactRequest - - // onOpenProfilePopup: { - // Global.openProfilePopup(publicKey) - // } - - // onOpenChangeNicknamePopup: { - // Global.openProfilePopup(publicKey, null, true) - // } // } // Item { @@ -279,11 +280,12 @@ SettingsContentBase { width: parent.width height: (contactsListHeight+50) searchString: searchBox.text + contactsStore: root.contactsStore + onOpenContactContextMenu: function (publicKey, name, icon) { + root.openContextMenu(publicKey, name, icon) + } contactsModel: root.contactsStore.blockedContactsModel panelUsage: Constants.contactsPanelUsage.blockedContacts - onOpenProfilePopup: { - Global.openProfilePopup(publicKey) - } } } diff --git a/ui/imports/shared/popups/ProfilePopup.qml b/ui/imports/shared/popups/ProfilePopup.qml index 366fecc831..27a72bd28a 100644 --- a/ui/imports/shared/popups/ProfilePopup.qml +++ b/ui/imports/shared/popups/ProfilePopup.qml @@ -112,14 +112,16 @@ StatusModal { showFooter = !isCurrentUser; popup.open(); - if (state == "openNickname") { - nicknamePopup.open(); - } else if (state == "contactRequest") { + if (state === "openNickname") { + profileView.nicknamePopup.open(); + } else if (state === "contactRequest") { sendContactRequestModal.open() - } else if (state == "blockUser") { + } else if (state === "blockUser") { blockUser(); - } else if (state == "unblockUser") { + } else if (state === "unblockUser") { unblockUser(); + } else if (state === "verifyIdentity") { + showVerifyIdentitySection = true; } } @@ -409,7 +411,7 @@ StatusModal { visible: showIdentityVerified || showIdentityVerifiedUntrustworthy text: qsTr("Rename") onClicked: { - nicknamePopup.open() + profileView.nicknamePopup.open() } }, diff --git a/ui/imports/shared/views/ProfileView.qml b/ui/imports/shared/views/ProfileView.qml index 4727cc14fd..71fd78a313 100644 --- a/ui/imports/shared/views/ProfileView.qml +++ b/ui/imports/shared/views/ProfileView.qml @@ -72,6 +72,7 @@ Rectangle { readonly property alias wizardAnimation: wizardAnimation readonly property alias challengeTxt: challengeTxt readonly property alias stepsListModel: stepsListModel + readonly property alias nicknamePopup: nicknamePopup readonly property int animationDuration: 500 diff --git a/ui/imports/shared/views/chat/MessageContextMenuView.qml b/ui/imports/shared/views/chat/MessageContextMenuView.qml index 53c914763d..fbe0408738 100644 --- a/ui/imports/shared/views/chat/MessageContextMenuView.qml +++ b/ui/imports/shared/views/chat/MessageContextMenuView.qml @@ -24,20 +24,21 @@ StatusPopupMenu { property string myPublicKey: "" property bool amIChatAdmin: false - property bool pinMessageAllowedForMembers: false + + property string selectedUserPublicKey: "" + property string selectedUserDisplayName: "" + property string selectedUserIcon: "" property int chatType: Constants.chatType.publicChat property string messageId: "" property string messageSenderId: "" property int messageContentType: Constants.messageContentType.unknownContentType - property string selectedUserPublicKey: "" - property string selectedUserDisplayName: "" - property string selectedUserIcon: "" property string imageSource: "" property bool isProfile: false property bool isRightClickOnImage: false property bool pinnedPopup: false + property bool pinMessageAllowedForMembers: false property bool isDebugEnabled: false property bool isEmoji: false property bool isSticker: false @@ -46,17 +47,45 @@ StatusPopupMenu { property bool canPin: false readonly property bool isMyMessage: { - return root.messageSenderId !== "" && root.messageSenderId == root.myPublicKey; + return root.messageSenderId !== "" && root.messageSenderId === root.myPublicKey; } readonly property bool isMe: { - return root.selectedUserPublicKey == root.store.contactsStore.myPublicKey; + return root.selectedUserPublicKey === root.store.contactsStore.myPublicKey; } - readonly property bool isMyMutualContact: { - return root.selectedUserPublicKey !== "" && root.store.contactsStore.isMyMutualContact(root.selectedUserPublicKey); + readonly property var contactDetails: { + if (root.selectedUserPublicKey === "" || isMe) { + return {} + } + return Utils.getContactDetailsAsJson(root.selectedUserPublicKey); + } + readonly property bool isContact: { + return root.selectedUserPublicKey !== "" && !!contactDetails.isContact } readonly property bool isBlockedContact: d.contactDetails && d.contactDetails.isBlocked + readonly property bool isBlockedContact: { + return root.selectedUserPublicKey !== "" && !!contactDetails.isBlocked + } + readonly property int outgoingVerificationStatus: { + if (root.selectedUserPublicKey === "" || root.isMe || !root.isContact) { + return 0 + } + return contactDetails.verificationStatus + } readonly property bool hasPendingContactRequest: { - return root.selectedUserPublicKey !== "" && root.store.contactsStore.hasPendingContactRequest(root.selectedUserPublicKey); + return !root.isMe && root.selectedUserPublicKey !== "" && + root.store.contactsStore.hasPendingContactRequest(root.selectedUserPublicKey); + } + readonly property bool hasReceivedVerificationRequestFrom: { + if (!root.selectedUserPublicKey || root.isMe || !root.isContact) { + return false + } + return root.store.contactsStore.hasReceivedVerificationRequestFrom(root.selectedUserPublicKey) + } + readonly property bool isVerificationRequestSent: { + if (!root.selectedUserPublicKey || root.isMe || !root.isContact) { + return false + } + return root.outgoingVerificationStatus !== Constants.verificationStatus.unverified } readonly property bool userTrustIsUnknown: d.contactDetails && d.contactDetails.trustStatus === Constants.trustStatus.unknown @@ -212,7 +241,7 @@ StatusPopupMenu { SendMessageMenuItem { id: sendMessageMenuItem - enabled: root.isProfile && root.isMyMutualContact && !root.isBlockedContact + enabled: root.isProfile && root.isContact && !root.isBlockedContact onTriggered: { root.createOneToOneChat("", root.selectedUserPublicKey, "") root.close() @@ -220,7 +249,7 @@ StatusPopupMenu { } SendContactRequestMenuItem { - enabled: root.isProfile && !root.isMe && !root.isMyMutualContact + enabled: root.isProfile && !root.isMe && !root.isContact && !root.isBlockedContact && !root.hasPendingContactRequest onTriggered: { root.openProfileClicked(root.selectedUserPublicKey, "contactRequest") @@ -238,6 +267,18 @@ StatusPopupMenu { } } + StatusMenuItem { + text: qsTr("Verify Identity") + icon.name: "checkmark-circle" + enabled: root.isProfile && !root.isMe && root.isContact + && !root.isBlockedContact && !root.isVerificationRequestSent + && !root.hasReceivedVerificationRequestFrom + onTriggered: { + root.openProfileClicked(root.selectedUserPublicKey, "verifyIdentity") + root.close() + } + } + StatusMenuItem { text: qsTr("Unblock User") icon.name: "remove-circle"