feat(contacts): add respond to request and pending contact menu options

Fixes #6251

Adds the "Respond to Contact Request" and "Contact Request Pending" options to the MessageContextMenu

Also fixes some small issues with contact verification where the state of the incoming request was not correct
This commit is contained in:
Jonathan Rainville 2022-07-07 15:44:54 -04:00
parent 03780d4875
commit 16b2ca5c2c
8 changed files with 116 additions and 46 deletions

View File

@ -321,3 +321,6 @@ proc getCommunityById*(self: Controller, communityId: string): CommunityDto =
proc getStatusForContactWithId*(self: Controller, publicKey: string): StatusUpdateDto = proc getStatusForContactWithId*(self: Controller, publicKey: string): StatusUpdateDto =
return self.contactsService.getStatusForContactWithId(publicKey) return self.contactsService.getStatusForContactWithId(publicKey)
proc getVerificationRequestFrom*(self: Controller, publicKey: string): VerificationRequest =
self.contactsService.getVerificationRequestFrom(publicKey)

View File

@ -202,7 +202,8 @@ method onStatusUrlRequested*(self: AccessInterface, action: StatusUrlAction, com
url: string, userId: string, groupName: string, listOfUserIds: seq[string]) {.base.} = url: string, userId: string, groupName: string, listOfUserIds: seq[string]) {.base.} =
raise newException(ValueError, "No implementation available") 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 # This way (using concepts) is used only for the modules managed by AppController
type type

View File

@ -677,9 +677,13 @@ method communityEdited*[T](
let channelGroup = community.toChannelGroupDto() let channelGroup = community.toChannelGroupDto()
self.view.editItem(self.createChannelGroupItem(channelGroup)) 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 = 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 (name, _, _) = self.controller.getContactNameAndImage(contact.id)
let request = self.getVerificationRequestFrom(publicKey)
let jsonObj = %* { let jsonObj = %* {
"displayName": name, "displayName": name,
"displayIcon": contact.image.thumbnail, "displayIcon": contact.image.thumbnail,
@ -699,7 +703,9 @@ method getContactDetailsAsJson*[T](self: Module[T], publicKey: string): string =
"isSyncing": contact.isSyncing, "isSyncing": contact.isSyncing,
"removed": contact.removed, "removed": contact.removed,
"trustStatus": contact.trustStatus.int, "trustStatus": contact.trustStatus.int,
# TODO rename verificationStatus to outgoingVerificationStatus
"verificationStatus": contact.verificationStatus.int, "verificationStatus": contact.verificationStatus.int,
"incomingVerificationStatus": request.status.int,
"hasAddedUs": contact.hasAddedUs "hasAddedUs": contact.hasAddedUs
} }
return $jsonObj return $jsonObj

View File

@ -615,8 +615,9 @@ QtObject:
return self.receivedIdentityRequests[publicKey] return self.receivedIdentityRequests[publicKey]
let response = status_contacts.getVerificationRequestFrom(publicKey) let response = status_contacts.getVerificationRequestFrom(publicKey)
result = response.result.toVerificationRequest() if not response.result.isNil and response.result.kind == JObject:
self.receivedIdentityRequests[publicKey] = result result = response.result.toVerificationRequest()
self.receivedIdentityRequests[publicKey] = result
except Exception as e: except Exception as e:
let errDesription = e.msg let errDesription = e.msg
error "error obtaining verification request", errDesription error "error obtaining verification request", errDesription

View File

@ -32,7 +32,8 @@ StatusModal {
property string userEnsName: "" property string userEnsName: ""
property string userIcon: "" property string userIcon: ""
property int userTrustStatus: Constants.trustStatus.unknown 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 text: ""
property string challenge: "" property string challenge: ""
property string response: "" property string response: ""
@ -85,8 +86,9 @@ StatusModal {
userTrustStatus = contactDetails.trustStatus userTrustStatus = contactDetails.trustStatus
userTrustIsUnknown = contactDetails.trustStatus === Constants.trustStatus.unknown userTrustIsUnknown = contactDetails.trustStatus === Constants.trustStatus.unknown
userIsUntrustworthy = contactDetails.trustStatus === Constants.trustStatus.untrustworthy userIsUntrustworthy = contactDetails.trustStatus === Constants.trustStatus.untrustworthy
verificationStatus = contactDetails.verificationStatus outgoingVerificationStatus = contactDetails.verificationStatus
isVerificationSent = verificationStatus !== Constants.verificationStatus.unverified incomingVerificationStatus = contactDetails.incomingVerificationStatus
isVerificationSent = outgoingVerificationStatus !== Constants.verificationStatus.unverified
if (isContact && popup.contactsStore.hasReceivedVerificationRequestFrom(publicKey)) { if (isContact && popup.contactsStore.hasReceivedVerificationRequestFrom(publicKey)) {
popup.hasReceivedVerificationRequest = true popup.hasReceivedVerificationRequest = true
@ -95,7 +97,7 @@ StatusModal {
if(isContact && isVerificationSent) { if(isContact && isVerificationSent) {
let verificationDetails = popup.contactsStore.getSentVerificationDetailsAsJson(publicKey); let verificationDetails = popup.contactsStore.getSentVerificationDetailsAsJson(publicKey);
verificationStatus = verificationDetails.requestStatus; outgoingVerificationStatus = verificationDetails.requestStatus;
verificationChallenge = verificationDetails.challenge; verificationChallenge = verificationDetails.challenge;
verificationResponse = verificationDetails.response; verificationResponse = verificationDetails.response;
verificationResponseDisplayName = verificationDetails.displayName; verificationResponseDisplayName = verificationDetails.displayName;
@ -103,8 +105,9 @@ StatusModal {
verificationRequestedAt = verificationDetails.requestedAt; verificationRequestedAt = verificationDetails.requestedAt;
verificationRepliedAt = verificationDetails.repliedAt; verificationRepliedAt = verificationDetails.repliedAt;
} }
isTrusted = verificationStatus === Constants.verificationStatus.trusted isTrusted = outgoingVerificationStatus === Constants.verificationStatus.trusted
isVerified = verificationStatus === Constants.verificationStatus.verified || incomingVerificationStatus === Constants.verificationStatus.trusted
isVerified = outgoingVerificationStatus === Constants.verificationStatus.verified
text = ""; // this is most likely unneeded text = ""; // this is most likely unneeded
isCurrentUser = popup.profileStore.pubkey === publicKey; isCurrentUser = popup.profileStore.pubkey === publicKey;
@ -112,16 +115,21 @@ StatusModal {
showFooter = !isCurrentUser; showFooter = !isCurrentUser;
popup.open(); popup.open();
if (state === "openNickname") { if (state === Constants.profilePopupStates.openNickname) {
profileView.nicknamePopup.open(); profileView.nicknamePopup.open();
} else if (state === "contactRequest") { } else if (state === Constants.profilePopupStates.contactRequest) {
sendContactRequestModal.open() sendContactRequestModal.open()
} else if (state === "blockUser") { } else if (state === Constants.profilePopupStates.blockUser) {
blockUser(); blockUser();
} else if (state === "unblockUser") { } else if (state === Constants.profilePopupStates.unblockUser) {
unblockUser(); unblockUser();
} else if (state === "verifyIdentity") { } else if (state === Constants.profilePopupStates.verifyIdentity) {
showVerifyIdentitySection = true; 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(); 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 width: 700
header.title: { header.title: {
@ -186,7 +211,7 @@ StatusModal {
hasReceivedVerificationRequest: popup.hasReceivedVerificationRequest hasReceivedVerificationRequest: popup.hasReceivedVerificationRequest
userTrustStatus: popup.userTrustStatus userTrustStatus: popup.userTrustStatus
verificationStatus: popup.verificationStatus outgoingVerificationStatus: popup.outgoingVerificationStatus
showVerifyIdentitySection: popup.showVerifyIdentitySection showVerifyIdentitySection: popup.showVerifyIdentitySection
showVerificationPendingSection: popup.showVerificationPendingSection showVerificationPendingSection: popup.showVerificationPendingSection
@ -354,20 +379,7 @@ StatusModal {
(hasReceivedVerificationRequest && !isTrusted) (hasReceivedVerificationRequest && !isTrusted)
onClicked: { onClicked: {
if (hasReceivedVerificationRequest) { if (hasReceivedVerificationRequest) {
try { popup.openPendingRequestPopup()
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)
}
} else { } else {
popup.showVerificationPendingSection = true popup.showVerificationPendingSection = true
profileView.wizardAnimation.running = true profileView.wizardAnimation.running = true

View File

@ -39,7 +39,7 @@ Rectangle {
property bool isAddedContact: false property bool isAddedContact: false
property int userTrustStatus: Constants.trustStatus.unknown property int userTrustStatus: Constants.trustStatus.unknown
property int verificationStatus: Constants.verificationStatus.unverified property int outgoingVerificationStatus: Constants.verificationStatus.unverified
property string challenge: "" property string challenge: ""
property string response: "" property string response: ""
@ -140,7 +140,7 @@ Rectangle {
} }
ScriptAction { ScriptAction {
script: { script: {
if (verificationStatus === Constants.verificationStatus.trusted) { if (outgoingVerificationStatus === Constants.verificationStatus.trusted) {
stepsListModel.setProperty(2, "stepCompleted", true); stepsListModel.setProperty(2, "stepCompleted", true);
} }
} }

View File

@ -62,15 +62,19 @@ StatusPopupMenu {
return root.selectedUserPublicKey !== "" && !!contactDetails.isContact return root.selectedUserPublicKey !== "" && !!contactDetails.isContact
} }
readonly property bool isBlockedContact: d.contactDetails && d.contactDetails.isBlocked readonly property bool isBlockedContact: d.contactDetails && d.contactDetails.isBlocked
readonly property bool isBlockedContact: {
return root.selectedUserPublicKey !== "" && !!contactDetails.isBlocked
}
readonly property int outgoingVerificationStatus: { readonly property int outgoingVerificationStatus: {
if (root.selectedUserPublicKey === "" || root.isMe || !root.isContact) { if (root.selectedUserPublicKey === "" || root.isMe || !root.isContact) {
return 0 return 0
} }
return contactDetails.verificationStatus return contactDetails.verificationStatus
} }
readonly property int incomingVerificationStatus: {
if (root.selectedUserPublicKey === "" || root.isMe || !root.isContact) {
return 0
}
return contactDetails.incomingVerificationStatus
}
readonly property bool hasPendingContactRequest: { readonly property bool hasPendingContactRequest: {
return !root.isMe && root.selectedUserPublicKey !== "" && return !root.isMe && root.selectedUserPublicKey !== "" &&
root.store.contactsStore.hasPendingContactRequest(root.selectedUserPublicKey); root.store.contactsStore.hasPendingContactRequest(root.selectedUserPublicKey);
@ -87,6 +91,13 @@ StatusPopupMenu {
} }
return root.outgoingVerificationStatus !== Constants.verificationStatus.unverified 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 userTrustIsUnknown: d.contactDetails && d.contactDetails.trustStatus === Constants.trustStatus.unknown
readonly property bool userIsUntrustworthy: d.contactDetails && d.contactDetails.trustStatus === Constants.trustStatus.untrustworthy readonly property bool userIsUntrustworthy: d.contactDetails && d.contactDetails.trustStatus === Constants.trustStatus.untrustworthy
@ -252,17 +263,8 @@ StatusPopupMenu {
enabled: root.isProfile && !root.isMe && !root.isContact enabled: root.isProfile && !root.isMe && !root.isContact
&& !root.isBlockedContact && !root.hasPendingContactRequest && !root.isBlockedContact && !root.hasPendingContactRequest
onTriggered: { onTriggered: {
root.openProfileClicked(root.selectedUserPublicKey, "contactRequest") root.openProfileClicked(root.selectedUserPublicKey,
root.close() Constants.profilePopupStates.contactRequest)
}
}
StatusMenuItem {
text: qsTr("Rename")
icon.name: "edit_pencil"
enabled: root.isProfile && !root.isMe
onTriggered: {
root.openProfileClicked(root.selectedUserPublicKey, "openNickname")
root.close() root.close()
} }
} }
@ -274,7 +276,42 @@ StatusPopupMenu {
&& !root.isBlockedContact && !root.isVerificationRequestSent && !root.isBlockedContact && !root.isVerificationRequestSent
&& !root.hasReceivedVerificationRequestFrom && !root.hasReceivedVerificationRequestFrom
onTriggered: { 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() root.close()
} }
} }

View File

@ -153,6 +153,16 @@ QtObject {
readonly property int blockedContacts: 6 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 QtObject validators: QtObject {
readonly property list<StatusValidator> displayName: [ readonly property list<StatusValidator> displayName: [
StatusMinLengthValidator { StatusMinLengthValidator {