diff --git a/src/app/modules/main/controller.nim b/src/app/modules/main/controller.nim index fb10524c49..62b58bb8b1 100644 --- a/src/app/modules/main/controller.nim +++ b/src/app/modules/main/controller.nim @@ -321,3 +321,6 @@ proc getCommunityById*(self: Controller, communityId: string): CommunityDto = proc getStatusForContactWithId*(self: Controller, publicKey: string): StatusUpdateDto = return self.contactsService.getStatusForContactWithId(publicKey) + +proc getVerificationRequestFrom*(self: Controller, publicKey: string): VerificationRequest = + self.contactsService.getVerificationRequestFrom(publicKey) diff --git a/src/app/modules/main/io_interface.nim b/src/app/modules/main/io_interface.nim index 256b027290..db088d6402 100644 --- a/src/app/modules/main/io_interface.nim +++ b/src/app/modules/main/io_interface.nim @@ -202,7 +202,8 @@ method onStatusUrlRequested*(self: AccessInterface, action: StatusUrlAction, com url: string, userId: string, groupName: string, listOfUserIds: seq[string]) {.base.} = raise newException(ValueError, "No implementation available") - +method getVerificationRequestFrom*(self: AccessInterface, publicKey: string): VerificationRequest {.base.} = + raise newException(ValueError, "No implementation available") # This way (using concepts) is used only for the modules managed by AppController type diff --git a/src/app/modules/main/module.nim b/src/app/modules/main/module.nim index de52ebf34a..4d2da29298 100644 --- a/src/app/modules/main/module.nim +++ b/src/app/modules/main/module.nim @@ -677,9 +677,13 @@ method communityEdited*[T]( let channelGroup = community.toChannelGroupDto() self.view.editItem(self.createChannelGroupItem(channelGroup)) +method getVerificationRequestFrom*[T](self: Module[T], publicKey: string): VerificationRequest = + self.controller.getVerificationRequestFrom(publicKey) + method getContactDetailsAsJson*[T](self: Module[T], publicKey: string): string = let contact = self.controller.getContact(publicKey) let (name, _, _) = self.controller.getContactNameAndImage(contact.id) + let request = self.getVerificationRequestFrom(publicKey) let jsonObj = %* { "displayName": name, "displayIcon": contact.image.thumbnail, @@ -699,7 +703,9 @@ method getContactDetailsAsJson*[T](self: Module[T], publicKey: string): string = "isSyncing": contact.isSyncing, "removed": contact.removed, "trustStatus": contact.trustStatus.int, + # TODO rename verificationStatus to outgoingVerificationStatus "verificationStatus": contact.verificationStatus.int, + "incomingVerificationStatus": request.status.int, "hasAddedUs": contact.hasAddedUs } return $jsonObj diff --git a/src/app_service/service/contacts/service.nim b/src/app_service/service/contacts/service.nim index e46ca1c489..50cf8bca89 100644 --- a/src/app_service/service/contacts/service.nim +++ b/src/app_service/service/contacts/service.nim @@ -615,8 +615,9 @@ QtObject: return self.receivedIdentityRequests[publicKey] let response = status_contacts.getVerificationRequestFrom(publicKey) - result = response.result.toVerificationRequest() - self.receivedIdentityRequests[publicKey] = result + if not response.result.isNil and response.result.kind == JObject: + result = response.result.toVerificationRequest() + self.receivedIdentityRequests[publicKey] = result except Exception as e: let errDesription = e.msg error "error obtaining verification request", errDesription diff --git a/ui/imports/shared/popups/ProfilePopup.qml b/ui/imports/shared/popups/ProfilePopup.qml index 27a72bd28a..2e21b546b4 100644 --- a/ui/imports/shared/popups/ProfilePopup.qml +++ b/ui/imports/shared/popups/ProfilePopup.qml @@ -32,7 +32,8 @@ StatusModal { property string userEnsName: "" property string userIcon: "" property int userTrustStatus: Constants.trustStatus.unknown - property int verificationStatus: Constants.verificationStatus.unverified + property int outgoingVerificationStatus: Constants.verificationStatus.unverified + property int incomingVerificationStatus: Constants.verificationStatus.unverified property string text: "" property string challenge: "" property string response: "" @@ -85,8 +86,9 @@ StatusModal { userTrustStatus = contactDetails.trustStatus userTrustIsUnknown = contactDetails.trustStatus === Constants.trustStatus.unknown userIsUntrustworthy = contactDetails.trustStatus === Constants.trustStatus.untrustworthy - verificationStatus = contactDetails.verificationStatus - isVerificationSent = verificationStatus !== Constants.verificationStatus.unverified + outgoingVerificationStatus = contactDetails.verificationStatus + incomingVerificationStatus = contactDetails.incomingVerificationStatus + isVerificationSent = outgoingVerificationStatus !== Constants.verificationStatus.unverified if (isContact && popup.contactsStore.hasReceivedVerificationRequestFrom(publicKey)) { popup.hasReceivedVerificationRequest = true @@ -95,7 +97,7 @@ StatusModal { if(isContact && isVerificationSent) { let verificationDetails = popup.contactsStore.getSentVerificationDetailsAsJson(publicKey); - verificationStatus = verificationDetails.requestStatus; + outgoingVerificationStatus = verificationDetails.requestStatus; verificationChallenge = verificationDetails.challenge; verificationResponse = verificationDetails.response; verificationResponseDisplayName = verificationDetails.displayName; @@ -103,8 +105,9 @@ StatusModal { verificationRequestedAt = verificationDetails.requestedAt; verificationRepliedAt = verificationDetails.repliedAt; } - isTrusted = verificationStatus === Constants.verificationStatus.trusted - isVerified = verificationStatus === Constants.verificationStatus.verified + isTrusted = outgoingVerificationStatus === Constants.verificationStatus.trusted + || incomingVerificationStatus === Constants.verificationStatus.trusted + isVerified = outgoingVerificationStatus === Constants.verificationStatus.verified text = ""; // this is most likely unneeded isCurrentUser = popup.profileStore.pubkey === publicKey; @@ -112,16 +115,21 @@ StatusModal { showFooter = !isCurrentUser; popup.open(); - if (state === "openNickname") { + if (state === Constants.profilePopupStates.openNickname) { profileView.nicknamePopup.open(); - } else if (state === "contactRequest") { + } else if (state === Constants.profilePopupStates.contactRequest) { sendContactRequestModal.open() - } else if (state === "blockUser") { + } else if (state === Constants.profilePopupStates.blockUser) { blockUser(); - } else if (state === "unblockUser") { + } else if (state === Constants.profilePopupStates.unblockUser) { unblockUser(); - } else if (state === "verifyIdentity") { + } else if (state === Constants.profilePopupStates.verifyIdentity) { showVerifyIdentitySection = true; + } else if (state === Constants.profilePopupStates.respondToPendingRequest) { + popup.openPendingRequestPopup() + } else if (state === Constants.profilePopupStates.showVerificationPendingSection) { + popup.showVerificationPendingSection = true + profileView.wizardAnimation.running = true } } @@ -137,6 +145,23 @@ StatusModal { profileView.unblockContactConfirmationDialog.open(); } + function openPendingRequestPopup() { + try { + let request = popup.contactsStore.getVerificationDetailsFromAsJson(popup.userPublicKey) + Global.openPopup(contactVerificationRequestPopupComponent, { + senderPublicKey: request.from, + senderDisplayName: request.displayName, + senderIcon: request.icon, + challengeText: request.challenge, + responseText: request.response, + messageTimestamp: request.requestedAt, + responseTimestamp: request.repliedAt + }) + } catch (e) { + console.error("Error getting or parsing verification data", e) + } + } + width: 700 header.title: { @@ -186,7 +211,7 @@ StatusModal { hasReceivedVerificationRequest: popup.hasReceivedVerificationRequest userTrustStatus: popup.userTrustStatus - verificationStatus: popup.verificationStatus + outgoingVerificationStatus: popup.outgoingVerificationStatus showVerifyIdentitySection: popup.showVerifyIdentitySection showVerificationPendingSection: popup.showVerificationPendingSection @@ -354,20 +379,7 @@ StatusModal { (hasReceivedVerificationRequest && !isTrusted) onClicked: { if (hasReceivedVerificationRequest) { - try { - let request = popup.contactsStore.getVerificationDetailsFromAsJson(popup.userPublicKey) - Global.openPopup(contactVerificationRequestPopupComponent, { - senderPublicKey: request.from, - senderDisplayName: request.displayName, - senderIcon: request.icon, - challengeText: request.challenge, - responseText: request.response, - messageTimestamp: request.requestedAt, - responseTimestamp: request.repliedAt - }) - } catch (e) { - console.error("Error getting or parsing verification data", e) - } + popup.openPendingRequestPopup() } else { popup.showVerificationPendingSection = true profileView.wizardAnimation.running = true diff --git a/ui/imports/shared/views/ProfileView.qml b/ui/imports/shared/views/ProfileView.qml index 71fd78a313..8cae43775c 100644 --- a/ui/imports/shared/views/ProfileView.qml +++ b/ui/imports/shared/views/ProfileView.qml @@ -39,7 +39,7 @@ Rectangle { property bool isAddedContact: false property int userTrustStatus: Constants.trustStatus.unknown - property int verificationStatus: Constants.verificationStatus.unverified + property int outgoingVerificationStatus: Constants.verificationStatus.unverified property string challenge: "" property string response: "" @@ -140,7 +140,7 @@ Rectangle { } ScriptAction { script: { - if (verificationStatus === Constants.verificationStatus.trusted) { + if (outgoingVerificationStatus === Constants.verificationStatus.trusted) { stepsListModel.setProperty(2, "stepCompleted", true); } } diff --git a/ui/imports/shared/views/chat/MessageContextMenuView.qml b/ui/imports/shared/views/chat/MessageContextMenuView.qml index fbe0408738..5bafe8f07b 100644 --- a/ui/imports/shared/views/chat/MessageContextMenuView.qml +++ b/ui/imports/shared/views/chat/MessageContextMenuView.qml @@ -62,15 +62,19 @@ StatusPopupMenu { 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 int incomingVerificationStatus: { + if (root.selectedUserPublicKey === "" || root.isMe || !root.isContact) { + return 0 + } + return contactDetails.incomingVerificationStatus + } readonly property bool hasPendingContactRequest: { return !root.isMe && root.selectedUserPublicKey !== "" && root.store.contactsStore.hasPendingContactRequest(root.selectedUserPublicKey); @@ -87,6 +91,13 @@ StatusPopupMenu { } return root.outgoingVerificationStatus !== Constants.verificationStatus.unverified } + readonly property bool isTrusted: { + if (!root.selectedUserPublicKey || root.isMe || !root.isContact) { + return false + } + return root.verificationStatus === Constants.verificationStatus.trusted || + root.incomingVerificationStatus === Constants.verificationStatus.trusted + } readonly property bool userTrustIsUnknown: d.contactDetails && d.contactDetails.trustStatus === Constants.trustStatus.unknown readonly property bool userIsUntrustworthy: d.contactDetails && d.contactDetails.trustStatus === Constants.trustStatus.untrustworthy @@ -252,17 +263,8 @@ StatusPopupMenu { enabled: root.isProfile && !root.isMe && !root.isContact && !root.isBlockedContact && !root.hasPendingContactRequest onTriggered: { - root.openProfileClicked(root.selectedUserPublicKey, "contactRequest") - root.close() - } - } - - StatusMenuItem { - text: qsTr("Rename") - icon.name: "edit_pencil" - enabled: root.isProfile && !root.isMe - onTriggered: { - root.openProfileClicked(root.selectedUserPublicKey, "openNickname") + root.openProfileClicked(root.selectedUserPublicKey, + Constants.profilePopupStates.contactRequest) root.close() } } @@ -274,7 +276,42 @@ StatusPopupMenu { && !root.isBlockedContact && !root.isVerificationRequestSent && !root.hasReceivedVerificationRequestFrom onTriggered: { - root.openProfileClicked(root.selectedUserPublicKey, "verifyIdentity") + root.openProfileClicked(root.selectedUserPublicKey, + Constants.profilePopupStates.verifyIdentity) + root.close() + } + } + + StatusMenuItem { + text: isVerificationRequestSent || + root.incomingVerificationStatus === Constants.verificationStatus.verified ? + qsTr("ID Request Pending....") : + qsTr("Respond to ID Request...") + icon.name: "checkmark-circle" + enabled: root.isProfile && !root.isMe && root.isContact + && !root.isBlockedContact && !root.isTrusted + && (root.hasReceivedVerificationRequestFrom + || root.isVerificationRequestSent) + onTriggered: { + if (hasReceivedVerificationRequestFrom) { + root.openProfileClicked(root.selectedUserPublicKey, + Constants.profilePopupStates.respondToPendingRequest) + } else if (root.isVerificationRequestSent) { + root.openProfileClicked(root.selectedUserPublicKey, + Constants.profilePopupStates.showVerificationPendingSection) + } + + root.close() + } + } + + StatusMenuItem { + text: qsTr("Rename") + icon.name: "edit_pencil" + enabled: root.isProfile && !root.isMe + onTriggered: { + root.openProfileClicked(root.selectedUserPublicKey, + Constants.profilePopupStates.openNickname) root.close() } } diff --git a/ui/imports/utils/Constants.qml b/ui/imports/utils/Constants.qml index ea3bffb658..9d323d0fb4 100644 --- a/ui/imports/utils/Constants.qml +++ b/ui/imports/utils/Constants.qml @@ -153,6 +153,16 @@ QtObject { readonly property int blockedContacts: 6 } + readonly property QtObject profilePopupStates: QtObject { + readonly property string openNickname: "openNickname" + readonly property string contactRequest: "contactRequest" + readonly property string blockUser: "blockUser" + readonly property string unblockUser: "unblockUser" + readonly property string verifyIdentity: "verifyIdentity" + readonly property string showVerificationPendingSection: "showVerificationPendingSection" + readonly property string respondToPendingRequest: "respondToPendingRequest" + } + readonly property QtObject validators: QtObject { readonly property list displayName: [ StatusMinLengthValidator {