feat(Profile flow): ID verification flows (incoming/outgoing)

- Send/request ID verification
- Reply to incoming ID request
- Review ID verification reply
- adjust SB and add shortcuts to trigger the various states of the ID
verification flows

Fixes #13709
Fixes #13745
Fixes #13747
This commit is contained in:
Lukáš Tinkl 2024-02-26 19:54:05 +01:00 committed by Lukáš Tinkl
parent 7cda470089
commit fda1d37c5b
20 changed files with 425 additions and 373 deletions

View File

@ -30,6 +30,8 @@ SplitView {
function getCompressedPk(publicKey) { return "zx3sh" + publicKey }
function getColorHashAsJson(publicKey, skipEnsVerification=false) {
if (skipEnsVerification)
return
return JSON.stringify([{colorId: 0, segmentLength: 1},
{colorId: 19, segmentLength: 2}])
}
@ -82,8 +84,8 @@ SplitView {
isBlocked: ctrlIsBlocked.checked,
isSyncing: false,
trustStatus: ctrlTrustStatus.currentValue,
verificationStatus: Constants.verificationStatus.unverified,
incomingVerificationStatus: Constants.verificationStatus.unverified,
verificationStatus: ctrlVerificationStatus.currentValue,
incomingVerificationStatus: ctrlIncomingVerificationStatus.currentValue,
contactRequestState: ctrlContactRequestState.currentValue,
bio: bio.text,
socialLinks: JSON.stringify
@ -110,12 +112,22 @@ SplitView {
}
}
Component.onCompleted: {
Global.userProfile = {
name: "Anna",
pubKey: "Oxdeadbeef",
icon: ModelsData.collectibles.cryptoPunks
}
}
Logs { id: logs }
Popups {
popupParent: root
rootStore: QtObject {
property var contactStore: QtObject {
property var contactsModule: null
function changeContactNickname(publicKey, newNickname, displayName, isEdit) {
logs.logEvent("rootStore::contactsStore::changeContactNickname", ["publicKey", "newNickname", "displayName", "isEdit"], arguments)
localNickname.text = newNickname
@ -138,26 +150,86 @@ SplitView {
function sendVerificationRequest(publicKey, challenge) {
logs.logEvent("rootStore::contactStore::sendVerificationRequest", ["publicKey", "challenge"], arguments)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.verifying)
}
function markUntrustworthy(publicKey) {
logs.logEvent("rootStore::contactStore::markUntrustworthy", ["publicKey"], arguments)
ctrlTrustStatus.currentIndex = ctrlTrustStatus.indexOfValue(Constants.trustStatus.untrustworthy)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
}
function markAsTrusted(publicKey) {
logs.logEvent("rootStore::contactStore::markAsTrusted", ["publicKey"], arguments)
ctrlTrustStatus.currentIndex = ctrlTrustStatus.indexOfValue(Constants.trustStatus.trusted)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.trusted)
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.trusted)
}
function removeContact(publicKey) {
logs.logEvent("rootStore::contactStore::removeContact", ["publicKey"], arguments)
ctrlContactRequestState.currentIndex = ctrlContactRequestState.indexOfValue(Constants.ContactRequestState.None)
ctrlIsContact.checked = false
}
function verifiedTrusted(publicKey) {
logs.logEvent("rootStore::contactStore::verifiedTrusted", ["publicKey"], arguments)
ctrlTrustStatus.currentIndex = ctrlTrustStatus.indexOfValue(Constants.trustStatus.trusted)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.trusted)
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.trusted)
}
function removeTrustStatus(publicKey) {
logs.logEvent("rootStore::contactStore::removeTrustStatus", ["publicKey"], arguments)
ctrlTrustStatus.currentIndex = ctrlTrustStatus.indexOfValue(Constants.trustStatus.unknown)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
}
function cancelVerificationRequest(pubKey) {
logs.logEvent("rootStore::contactStore::cancelVerificationRequest", ["pubKey"], arguments)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
}
function declineVerificationRequest(pubKey) {
logs.logEvent("rootStore::contactStore::declineVerificationRequest", ["pubKey"], arguments)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
}
function acceptVerificationRequest(pubKey, response) {
logs.logEvent("rootStore::contactStore::acceptVerificationRequest", ["pubKey"], arguments)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.verifying)
}
function verifiedUntrustworthy(pubKey) {
logs.logEvent("rootStore::contactStore::verifiedUntrustworthy", ["pubKey"], arguments)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
ctrlTrustStatus.currentIndex = ctrlTrustStatus.indexOfValue(Constants.trustStatus.untrustworthy)
}
function getSentVerificationDetailsAsJson(pubKey) {
return {
requestStatus: ctrlVerificationStatus.currentValue,
challenge: "The real Alex would know this 100%! Whats my favourite colour?",
response: ctrlIncomingVerificationStatus.currentValue === Constants.verificationStatus.verified ? "Yellow!" : "",
displayName: ProfileUtils.displayName(localNickname.text, name.text, displayName.text),
icon: Style.png("status-logo"),
requestedAt: Date.now() - 86400000,
repliedAt: Date.now()
}
}
function getVerificationDetailsFromAsJson(pubKey) {
return {
from: "0xdeadbeef",
challenge: "The real Alex would know this 100%! Whats my favourite colour?",
response: "",
requestedAt: Date.now() - 86400000,
}
}
}
}
@ -231,6 +303,7 @@ SplitView {
function removeContact(publicKey) {
logs.logEvent("contactsStore::removeContact", ["publicKey"], arguments)
ctrlContactRequestState.currentIndex = ctrlContactRequestState.indexOfValue(Constants.ContactRequestState.None)
ctrlIsContact.checked = false
}
function acceptContactRequest(publicKey, contactRequestId) {
@ -257,6 +330,12 @@ SplitView {
ctrlTrustStatus.currentIndex = ctrlTrustStatus.indexOfValue(Constants.trustStatus.trusted)
}
function cancelVerificationRequest(pubKey) {
logs.logEvent("contactsStore::cancelVerificationRequest", ["pubKey"], arguments)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
}
function getLinkToProfile(publicKey) {
return Constants.userLinkPrefix + publicKey
}
@ -324,7 +403,7 @@ SplitView {
Label { text: "localNickname:" }
TextField {
id: localNickname
text: "Nick"
text: "Alex"
placeholderText: "Local Nickname"
}
Label { text: "displayName:" }
@ -333,6 +412,19 @@ SplitView {
text: "Alex Pella"
placeholderText: "Display Name"
}
CheckBox {
id: ensVerified
checked: true
text: "ensVerified"
}
Label { text: "name:" }
TextField {
id: name
enabled: ensVerified.checked
text: ensVerified.checked ? "mock-ens-name.eth" : ""
placeholderText: "ENS name"
}
}
RowLayout {
CheckBox {
@ -354,21 +446,16 @@ SplitView {
from: 0
to: 11 // Theme.palette.userCustomizationColors.length
}
}
RowLayout {
Layout.fillWidth: true
CheckBox {
id: ensVerified
checked: true
text: "ensVerified"
}
Label { text: "name:" }
TextField {
id: name
enabled: ensVerified.checked
text: ensVerified.checked ? "mock-ens-name.eth" : ""
placeholderText: "ENS name"
Label { text: "onlineStatus" }
ComboBox {
id: ctrlOnlineStatus
textRole: "text"
valueRole: "value"
model: [
{ value: Constants.onlineStatus.unknown, text: "unknown" },
{ value: Constants.onlineStatus.inactive, text: "inactive" },
{ value: Constants.onlineStatus.online, text: "online" }
]
}
}
RowLayout {
@ -392,14 +479,6 @@ SplitView {
{ value: Constants.ContactRequestState.Dismissed, text: "Dismissed" }
]
}
CheckBox {
id: ctrlIsBlocked
text: "isBlocked"
}
}
RowLayout {
Layout.fillWidth: true
enabled: !switchOwnProfile.checked
Label { text: "trustStatus:" }
ComboBox {
id: ctrlTrustStatus
@ -411,17 +490,78 @@ SplitView {
{ value: Constants.trustStatus.untrustworthy, text: "untrustworthy" }
]
}
Label { text: "onlineStatus" }
CheckBox {
id: ctrlIsBlocked
text: "isBlocked"
}
}
RowLayout {
Layout.fillWidth: true
Label { text: "incomingVerificationStatus:" }
ComboBox {
id: ctrlOnlineStatus
id: ctrlIncomingVerificationStatus
enabled: ctrlIsContact.checked && !switchOwnProfile.checked
textRole: "text"
valueRole: "value"
model: [
{ value: Constants.onlineStatus.unknown, text: "unknown" },
{ value: Constants.onlineStatus.inactive, text: "inactive" },
{ value: Constants.onlineStatus.online, text: "online" }
{ value: Constants.verificationStatus.unverified, text: "unverified" },
{ value: Constants.verificationStatus.verifying, text: "verifying" },
{ value: Constants.verificationStatus.verified, text: "verified" },
{ value: Constants.verificationStatus.declined, text: "declined" },
{ value: Constants.verificationStatus.canceled, text: "canceled" },
{ value: Constants.verificationStatus.trusted, text: "trusted" },
{ value: Constants.verificationStatus.untrustworthy, text: "untrustworthy" }
]
}
Label { text: "verificationStatus:" }
ComboBox {
id: ctrlVerificationStatus
enabled: ctrlIsContact.checked && !switchOwnProfile.checked
textRole: "text"
valueRole: "value"
model: [
{ value: Constants.verificationStatus.unverified, text: "unverified" },
{ value: Constants.verificationStatus.verifying, text: "verifying" },
{ value: Constants.verificationStatus.verified, text: "verified" },
{ value: Constants.verificationStatus.declined, text: "declined" },
{ value: Constants.verificationStatus.canceled, text: "canceled" },
{ value: Constants.verificationStatus.trusted, text: "trusted" },
{ value: Constants.verificationStatus.untrustworthy, text: "untrustworthy" }
]
}
Button {
text: "Send ID request"
onClicked: {
ctrlIsContact.checked = true
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
}
}
Button {
text: "Reply to ID request"
onClicked: {
ctrlIsContact.checked = true
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.verifying)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.unverified)
}
}
Button {
text: "Pending ID request"
onClicked: {
ctrlIsContact.checked = true
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.verifying)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.verifying)
}
}
Button {
text: "Review ID reply"
onClicked: {
ctrlIsContact.checked = true
ctrlIncomingVerificationStatus.currentIndex = ctrlIncomingVerificationStatus.indexOfValue(Constants.verificationStatus.verified)
ctrlVerificationStatus.currentIndex = ctrlVerificationStatus.indexOfValue(Constants.verificationStatus.verifying)
}
}
}
RowLayout {
Layout.fillWidth: true

View File

@ -73,8 +73,8 @@ StatusListItem {
width: visible ? implicitWidth : 0
height: visible ? implicitHeight : 0
text: verificationRequestStatus === Constants.verificationStatus.verifying ?
qsTr("Respond to ID Request") :
qsTr("See ID Request")
qsTr("Reply to ID verification request") :
qsTr("Review ID verification reply")
size: StatusBaseButton.Size.Small
onClicked: root.showVerificationRequest(root.publicKey)
},

View File

@ -81,7 +81,7 @@ StatusDialog {
title: !d.dbEncryptionInProgress ? qsTr("Re-encryption complete") :
qsTr("Re-encrypting your data with your new password...")
subTitle: !d.dbEncryptionInProgress ? qsTr("Restart Status and log in using your new password") :
qsTr("Do not quit the app of turn off your device")
qsTr("Do not quit the app or turn off your device")
statusListItemSubTitle.customColor: !d.passwordChanged ? Style.current.red : Theme.palette.successColor1
statusListItemIcon.active: d.passwordChanged
asset.name: "checkmark-circle"

View File

@ -96,7 +96,7 @@ QtObject {
icon: "profile"})
append({subsection: Constants.settingsSubsection.password,
text: qsTr("Password"),
icon: "profile"})
icon: "password"})
append({subsection: Constants.settingsSubsection.keycard,
text: qsTr("Keycard"),
icon: "keycard"})

View File

@ -55,7 +55,7 @@ SettingsContentBase {
store: ({contactsStore: root.contactsStore})
onOpenProfileClicked: function (pubkey) {
Global.openProfilePopup(pubkey, null)
Global.openProfilePopup(pubkey, null, null)
}
onCreateOneToOneChat: function (communityId, chatId, ensName) {
root.contactsStore.joinPrivateChat(chatId)
@ -216,7 +216,7 @@ SettingsContentBase {
}
onShowVerificationRequest: {
Global.openIncomingIDRequestPopup(publicKey, null)
Global.openIncomingIDRequestPopup(publicKey, null, null)
}
}
@ -320,4 +320,3 @@ SettingsContentBase {
}
}
}

View File

@ -183,11 +183,13 @@ QtObject {
openPopup(removeIDVerificationPopupComponent, {publicKey, contactDetails}, cb)
}
function openOutgoingIDRequestPopup(publicKey, cb) {
function openOutgoingIDRequestPopup(publicKey, contactDetails, cb) {
let details = contactDetails ?? Utils.getContactDetailsAsJson(publicKey)
try {
const verificationDetails = root.rootStore.profileSectionStore.contactsStore.getSentVerificationDetailsAsJson(publicKey)
const verificationDetails = rootStore.contactStore.getSentVerificationDetailsAsJson(publicKey)
const popupProperties = {
userPublicKey: publicKey,
publicKey: publicKey,
contactDetails: details,
verificationStatus: verificationDetails.requestStatus,
verificationChallenge: verificationDetails.challenge,
verificationResponse: verificationDetails.response,
@ -202,13 +204,9 @@ QtObject {
}
}
function openIncomingIDRequestPopup(publicKey, cb) {
const popupProperties = {
contactsStore: root.rootStore.profileSectionStore.contactsStore,
publicKey: publicKey
}
openPopup(contactVerificationRequestPopupComponent, popupProperties, cb)
function openIncomingIDRequestPopup(publicKey, contactDetails, cb) {
let details = contactDetails ?? Utils.getContactDetailsAsJson(publicKey)
openPopup(contactVerificationRequestPopupComponent, {publicKey, contactDetails: details})
}
function openInviteFriendsToCommunityPopup(community, communitySectionModule, cb) {
@ -357,7 +355,7 @@ QtObject {
onAccepted: {
rootStore.contactStore.removeContact(publicKey)
if (removeIDVerification)
rootStore.contactStore.cancelVerificationRequest(publicKey)
rootStore.contactStore.removeTrustStatus(publicKey)
if (markAsUntrusted) {
rootStore.contactStore.markUntrustworthy(publicKey)
Global.displaySuccessToastMessage(qsTr("%1 removed from contacts and marked as untrusted").arg(mainDisplayName))
@ -372,11 +370,14 @@ QtObject {
Component {
id: contactVerificationRequestPopupComponent
ContactVerificationRequestPopup {
onResponseSent: {
root.rootStore.profileSectionStore.contactsStore.acceptVerificationRequest(senderPublicKey, response)
contactsStore: rootStore.contactStore
onResponseSent: (senderPublicKey, response) => {
contactsStore.acceptVerificationRequest(senderPublicKey, response)
Global.displaySuccessToastMessage(qsTr("ID verification reply sent"))
}
onVerificationRefused: {
root.rootStore.profileSectionStore.contactsStore.declineVerificationRequest(senderPublicKey)
onVerificationRefused: (senderPublicKey) => {
contactsStore.declineVerificationRequest(senderPublicKey)
Global.displaySuccessToastMessage(qsTr("ID verification request declined"))
}
onClosed: destroy()
}
@ -386,13 +387,15 @@ QtObject {
id: contactOutgoingVerificationRequestPopupComponent
OutgoingContactVerificationRequestPopup {
onVerificationRequestCanceled: {
root.rootStore.profileSectionStore.contactsStore.cancelVerificationRequest(userPublicKey)
rootStore.contactStore.cancelVerificationRequest(publicKey)
}
onUntrustworthyVerified: {
root.rootStore.profileSectionStore.contactsStore.verifiedUntrustworthy(userPublicKey)
rootStore.contactStore.verifiedUntrustworthy(publicKey)
Global.displaySuccessToastMessage(qsTr("%1 marked as untrusted").arg(mainDisplayName))
}
onTrustedVerified: {
root.rootStore.profileSectionStore.contactsStore.verifiedTrusted(userPublicKey)
rootStore.contactStore.verifiedTrusted(publicKey)
Global.displaySuccessToastMessage(qsTr("%1 ID verified").arg(mainDisplayName))
}
onClosed: destroy()
}
@ -411,7 +414,7 @@ QtObject {
id: markAsIDVerifiedPopupComponent
MarkAsIDVerifiedDialog {
onAccepted: {
rootStore.contactStore.verifiedTrusted(publicKey)
rootStore.contactStore.markAsTrusted(publicKey)
Global.displaySuccessToastMessage(qsTr("%1 ID verified").arg(mainDisplayName))
close()
}
@ -576,8 +579,6 @@ QtObject {
MarkAsUntrustedPopup {
onAccepted: {
rootStore.contactStore.markUntrustworthy(publicKey)
if (removeIDVerification)
rootStore.contactStore.cancelVerificationRequest(publicKey)
if (removeContact) {
rootStore.contactStore.removeContact(publicKey)
Global.displaySuccessToastMessage(qsTr("%1 removed from contacts and marked as untrusted").arg(mainDisplayName))
@ -608,7 +609,7 @@ QtObject {
onAccepted: {
rootStore.contactStore.blockContact(publicKey)
if (removeIDVerification)
rootStore.contactStore.cancelVerificationRequest(publicKey)
rootStore.contactStore.removeTrustStatus(publicKey)
if (removeContact)
rootStore.contactStore.removeContact(publicKey)
Global.displaySuccessToastMessage(qsTr("%1 blocked").arg(mainDisplayName))

View File

@ -14,8 +14,8 @@ StatusFlatButton {
signal activate()
enabled: verificationStatus == Constants.verificationStatus.verifying ||
verificationStatus == Constants.verificationStatus.verified
enabled: verificationStatus === Constants.verificationStatus.verifying ||
verificationStatus === Constants.verificationStatus.verified
size: StatusBaseButton.Size.Small
text: {
switch (verificationStatus) {
@ -47,4 +47,4 @@ StatusFlatButton {
}
onClicked: root.activate()
}
}

View File

@ -58,7 +58,7 @@ ActivityNotificationMessage {
OutgoingContactVerificationCta {
verificationStatus: notification ? notification.verificationStatus : Constants.verificationStatus.unverified
onActivate: {
Global.openOutgoingIDRequestPopup(root.contactId, popup => {})
Global.openOutgoingIDRequestPopup(root.contactId, root.contactDetails, null)
root.closeActivityCenter()
}
}
@ -70,9 +70,9 @@ ActivityNotificationMessage {
IncomingContactVerificationCta {
verificationStatus: notification ? notification.verificationStatus : Constants.verificationStatus.unverified
onActivate: {
Global.openIncomingIDRequestPopup(root.contactId, popup => {})
Global.openIncomingIDRequestPopup(root.contactId, root.contactDetails, null)
root.closeActivityCenter()
}
}
}
}
}

View File

@ -19,12 +19,8 @@ CommonContactDialog {
readonly property var d: QtObject {
id: d
readonly property int outgoingVerificationStatus: contactDetails.verificationStatus
readonly property int incomingVerificationStatus: contactDetails.incomingVerificationStatus
readonly property bool isVerificationRequestReceived: incomingVerificationStatus === Constants.verificationStatus.verifying ||
incomingVerificationStatus === Constants.verificationStatus.verified
readonly property bool isTrusted: outgoingVerificationStatus === Constants.verificationStatus.trusted ||
incomingVerificationStatus === Constants.verificationStatus.trusted
readonly property bool isTrusted: contactDetails.outgoingVerificationStatus === Constants.verificationStatus.trusted ||
contactDetails.incomingVerificationStatus === Constants.verificationStatus.trusted
}
StatusBaseText {
@ -58,7 +54,7 @@ CommonContactDialog {
StatusCheckBox {
id: ctrlRemoveIDVerification
visible: contactDetails.isContact && !d.isTrusted && d.isVerificationRequestReceived
visible: (contactDetails.isContact && d.isTrusted) || contactDetails.trustStatus === Constants.trustStatus.trusted
checked: visible
enabled: false
text: qsTr("Remove ID verification")

View File

@ -17,6 +17,7 @@ StatusDialog {
required property string publicKey
required property var contactDetails
property bool loadingContactDetails
default property alias content: contentLayout.children
@ -26,7 +27,7 @@ StatusDialog {
contactDetails.displayName, contactDetails.alias)
readonly property string optionalDisplayName: ProfileUtils.displayName("", contactDetails.name, contactDetails.displayName, contactDetails.alias)
width: 480
width: Math.max(implicitWidth, 480)
horizontalPadding: 0
topPadding: 20
bottomPadding: 0
@ -47,6 +48,7 @@ StatusDialog {
imageHeight: 60
ensVerified: contactDetails.ensVerified
onlineStatus: contactDetails.onlineStatus
loading: root.loadingContactDetails
}
ColumnLayout {

View File

@ -1,24 +1,21 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import QtQml.Models 2.14
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
import StatusQ.Controls 0.1
import StatusQ.Core 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import StatusQ.Popups.Dialog 0.1
import shared.controls 1.0
import shared.views.chat 1.0
import utils 1.0
StatusDialog {
CommonContactDialog {
id: root
property var contactsStore
property string publicKey
required property var contactsStore
signal verificationRefused(string senderPublicKey)
signal responseSent(string senderPublicKey, string response)
@ -32,19 +29,16 @@ StatusDialog {
}
d.senderPublicKey = request.from
d.senderDisplayName = request.displayName
d.senderIcon = request.icon
d.challengeText = request.challenge
d.responseText = request.response
d.messageTimestamp = request.requestedAt
d.responseTimestamp = request.repliedAt
} catch (e) {
console.error("Error getting or parsing verification data", e)
}
}
Connections {
target: root.contactsStore.receivedContactRequestsModel
readonly property var _con: Connections {
target: root.contactsStore.receivedContactRequestsModel ?? null
function onItemChanged(pubKey) {
if (pubKey === root.publicKey)
@ -52,134 +46,81 @@ StatusDialog {
}
}
QtObject {
readonly property var d: QtObject {
id: d
property string senderPublicKey: ""
property string senderDisplayName: ""
property string senderIcon: ""
property string challengeText: ""
property string responseText: ""
property string senderPublicKey
property string challengeText
property string responseText
property double messageTimestamp
property double responseTimestamp
}
title: qsTr("%1 is asking you to verify your identity").arg(d.senderDisplayName)
padding: 0
title: qsTr("Reply to ID verification request")
onAboutToShow: {
root.updateVerificationDetails()
verificationResponse.input.edit.forceActiveFocus(Qt.MouseFocusReason)
verificationResponse.input.edit.forceActiveFocus()
}
StatusScrollView {
id: scrollView
anchors.fill: parent
contentWidth: availableWidth
implicitWidth: 560
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: msgColumn.implicitHeight + msgColumn.anchors.topMargin + msgColumn.anchors.bottomMargin
color: "transparent"
border.width: 1
border.color: Theme.palette.baseColor2
radius: Style.current.radius
ColumnLayout {
width: scrollView.availableWidth
spacing: Style.current.padding
id: msgColumn
anchors.fill: parent
anchors.margins: Style.current.padding
StatusBaseText {
id: description
color: Theme.palette.directColor1
wrapMode: Text.WordWrap
text: qsTr("%1 would like to verify your identity. Answer the question to prove your identity to %2")
.arg(d.senderDisplayName).arg(d.senderDisplayName)
font.pixelSize: 15
StatusTimeStampLabel {
Layout.fillWidth: true
}
SimplifiedMessageView {
id: verificationMessage
timestamp: d.messageTimestamp
messageDetails.messageText: d.challengeText
messageDetails.sender.displayName: d.senderDisplayName
messageDetails.sender.profileImage.name: d.senderIcon
messageDetails.sender.profileImage.assetSettings.isImage: true
messageDetails.sender.profileImage.pubkey: d.senderPublicKey
messageDetails.sender.profileImage.colorId: Utils.colorIdForPubkey(d.senderPublicKey)
messageDetails.sender.profileImage.colorHash: Utils.getColorHashAsJson(d.senderPublicKey, Utils.isEnsVerified(d.senderPublicKey))
Layout.fillWidth: true
}
StatusInput {
id: verificationResponse
visible: !d.responseText
input.multiline: true
placeholderText: qsTr("Provide answer to verification request from this contact.")
minimumHeight: 152
maximumHeight: 152
input.verticalAlignment: TextEdit.AlignTop
charLimit: 280
Layout.fillWidth: true
}
SimplifiedMessageView {
id: responseMessage
visible: !!d.responseText
timestamp: d.responseTimestamp
messageDetails.messageText: d.responseText
messageDetails.sender.displayName: userProfile.displayName
messageDetails.sender.profileImage.name: userProfile.icon
messageDetails.sender.profileImage.assetSettings.isImage: true
messageDetails.sender.profileImage.pubkey: userProfile.pubKey
messageDetails.sender.profileImage.colorId: Utils.colorIdForPubkey(userProfile.pubKey)
messageDetails.sender.profileImage.colorHash: Utils.getColorHashAsJson(userProfile.pubKey, !!userProfile.preferredName)
Layout.fillWidth: true
}
StatusBaseText {
id: responseSent
visible: !!d.responseText
color: Theme.palette.baseColor1
wrapMode: Text.WordWrap
text: qsTr("Your answer has been sent to %1.").arg(d.senderDisplayName)
font.pixelSize: 13
horizontalAlignment: Text.AlignHCenter
Layout.fillWidth: true
wrapMode: Text.WordWrap
text: d.challengeText
}
}
}
footer: StatusDialogFooter {
rightButtons: ObjectModel {
StatusButton {
visible: !d.responseText
text: qsTr("Refuse Verification")
objectName: "refuseVerificationButton"
onClicked: {
root.verificationRefused(d.senderPublicKey)
root.close();
}
StatusInput {
id: verificationResponse
input.multiline: true
label: qsTr("Your answer")
placeholderText: qsTr("Write your answer...")
minimumHeight: 152
maximumHeight: 152
input.verticalAlignment: TextEdit.AlignTop
charLimit: 280
Layout.fillWidth: true
Layout.topMargin: Style.current.padding
}
rightButtons: ObjectModel {
StatusButton {
text: qsTr("Decline")
type: StatusBaseButton.Type.Danger
objectName: "refuseVerificationButton"
onClicked: {
root.verificationRefused(d.senderPublicKey)
root.close()
}
StatusButton {
text: qsTr("Send Answer")
objectName: "sendAnswerButton"
visible: !d.responseText
enabled: verificationResponse.text !== ""
onClicked: {
root.responseSent(d.senderPublicKey, SQUtils.StringUtils.escapeHtml(verificationResponse.text))
d.responseText = verificationResponse.text
d.responseTimestamp = Date.now()
}
}
StatusFlatButton {
visible: d.responseText
text: qsTr("Change answer")
objectName: "changeAnswerButton"
onClicked: {
d.responseText = ""
}
}
StatusButton {
visible: d.responseText
text: qsTr("Close")
objectName: "closeButton"
onClicked: root.close()
}
StatusButton {
text: qsTr("Send reply")
type: StatusBaseButton.Type.Success
objectName: "sendAnswerButton"
enabled: verificationResponse.text !== ""
onClicked: {
root.responseSent(d.senderPublicKey, SQUtils.StringUtils.escapeHtml(verificationResponse.text))
d.responseText = verificationResponse.text
d.responseTimestamp = Date.now()
root.close()
}
}
}

View File

@ -1,5 +1,4 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15

View File

@ -21,8 +21,6 @@ CommonContactDialog {
id: d
readonly property int outgoingVerificationStatus: contactDetails.verificationStatus
readonly property int incomingVerificationStatus: contactDetails.incomingVerificationStatus
readonly property bool isVerificationRequestReceived: incomingVerificationStatus === Constants.verificationStatus.verifying ||
incomingVerificationStatus === Constants.verificationStatus.verified
readonly property bool isTrusted: outgoingVerificationStatus === Constants.verificationStatus.trusted ||
incomingVerificationStatus === Constants.verificationStatus.trusted
}
@ -36,7 +34,7 @@ CommonContactDialog {
StatusCheckBox {
id: ctrlRemoveIDVerification
visible: contactDetails.isContact && !d.isTrusted && d.isVerificationRequestReceived
visible: (contactDetails.isContact && d.isTrusted) || contactDetails.trustStatus === Constants.trustStatus.trusted
checked: visible
enabled: false
text: qsTr("Remove ID verification")

View File

@ -1,21 +1,18 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import QtQml.Models 2.14
import QtQuick 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
import utils 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups.Dialog 0.1
import shared.views.chat 1.0
StatusDialog {
CommonContactDialog {
id: root
/* required*/ property string userPublicKey
property int verificationStatus
property string verificationChallenge
property string verificationResponse
@ -24,96 +21,94 @@ StatusDialog {
property string verificationRequestedAt
property string verificationRepliedAt
signal verificationRequestCanceled(string userPublicKey)
signal untrustworthyVerified(string userPublicKey)
signal trustedVerified(string userPublicKey)
readonly property bool hasReply: root.verificationResponse !== ""
title: qsTr("Verify %1's Identity").arg(root.verificationResponseDisplayName)
padding: 0
signal verificationRequestCanceled(string publicKey)
signal untrustworthyVerified(string publicKey)
signal trustedVerified(string publicKey)
footer: StatusDialogFooter {
leftButtons: ObjectModel {
StatusButton {
text: qsTr("Cancel verification")
type: StatusBaseButton.Type.Danger
visible: root.verificationStatus !== Constants.verificationStatus.verified
onClicked: {
root.verificationRequestCanceled(root.userPublicKey)
root.close()
}
title: !hasReply ? qsTr("ID verification pending") : qsTr("Review ID verification reply")
rightButtons: ObjectModel {
StatusFlatButton {
text: qsTr("Cancel request")
type: StatusBaseButton.Type.Danger
borderColor: "transparent"
visible: !root.hasReply
onClicked: {
root.verificationRequestCanceled(root.publicKey)
root.close()
}
}
StatusButton {
text: qsTr("Done")
visible: !root.hasReply
onClicked: root.close()
}
rightButtons: ObjectModel {
StatusButton {
text: qsTr("Mark Untrustworthy")
enabled: root.verificationResponse !== ""
type: StatusBaseButton.Type.Danger
onClicked: {
root.untrustworthyVerified(root.userPublicKey)
root.close()
}
StatusButton {
text: qsTr("Mark as untrusted")
visible: root.hasReply
type: StatusBaseButton.Type.Danger
onClicked: {
root.untrustworthyVerified(root.publicKey)
root.close()
}
StatusButton {
text: qsTr("Confirm Identity")
enabled: root.verificationResponse !== ""
type: StatusBaseButton.Type.Primary
onClicked: {
root.trustedVerified(root.userPublicKey)
root.close()
}
}
StatusButton {
text: qsTr("Mark as verified")
visible: root.hasReply
type: StatusBaseButton.Type.Success
onClicked: {
root.trustedVerified(root.publicKey)
root.close()
}
}
}
StatusScrollView {
id: scrollView
anchors.fill: parent
SimplifiedMessageView {
id: challengeMessage
timestamp: root.verificationRequestedAt
messageDetails.messageText: root.verificationChallenge
messageDetails.sender.id: Global.userProfile.pubKey
messageDetails.sender.displayName: Global.userProfile.name
messageDetails.sender.profileImage.name: Global.userProfile.icon
messageDetails.sender.profileImage.assetSettings.isImage: true
messageDetails.sender.profileImage.colorId: Utils.colorIdForPubkey(Global.userProfile.pubKey)
messageDetails.sender.profileImage.colorHash: Utils.getColorHashAsJson(Global.userProfile.pubKey, !!Global.userProfile.preferredName)
messageDetails.sender.isEnsVerified: !!Global.userProfile.preferredName
Layout.fillWidth: true
}
contentWidth: availableWidth
implicitWidth: 560
SimplifiedMessageView {
id: responseMessage
visible: root.hasReply
timestamp: root.verificationRepliedAt
messageDetails.messageText: root.verificationResponse
messageDetails.sender.id: root.publicKey
messageDetails.sender.displayName: root.verificationResponseDisplayName
messageDetails.sender.profileImage.name: root.verificationResponseIcon
messageDetails.sender.profileImage.assetSettings.isImage: true
messageDetails.sender.profileImage.colorId: Utils.colorIdForPubkey(root.publicKey)
messageDetails.sender.profileImage.colorHash: Utils.getColorHashAsJson(root.publicKey)
messageDetails.sender.isEnsVerified: contactDetails.ensVerified
Layout.fillWidth: true
}
ColumnLayout {
width: scrollView.availableWidth
spacing: Style.current.padding
SimplifiedMessageView {
id: challengeMessage
timestamp: root.verificationRequestedAt
messageDetails.messageText: root.verificationChallenge
messageDetails.sender.displayName: userProfile.name
messageDetails.sender.profileImage.name: userProfile.icon
messageDetails.sender.profileImage.assetSettings.isImage: true
messageDetails.sender.profileImage.pubkey: userProfile.pubKey
messageDetails.sender.profileImage.colorId: Utils.colorIdForPubkey(userProfile.pubKey)
messageDetails.sender.profileImage.colorHash: Utils.getColorHashAsJson(userProfile.pubKey, !!userProfile.preferredName)
Layout.fillWidth: true
}
SimplifiedMessageView {
id: responseMessage
visible: root.verificationResponse !== ""
timestamp: root.verificationRepliedAt
messageDetails.messageText: root.verificationResponse
messageDetails.sender.displayName: root.verificationResponseDisplayName
messageDetails.sender.profileImage.name: root.verificationResponseIcon
messageDetails.sender.profileImage.assetSettings.isImage: true
messageDetails.sender.profileImage.pubkey: root.userPublicKey
messageDetails.sender.profileImage.colorId: Utils.colorIdForPubkey(root.userPublicKey)
messageDetails.sender.profileImage.colorHash: Utils.getColorHashAsJson(root.userPublicKey, Utils.isEnsVerified(root.userPublicKey))
Layout.fillWidth: true
}
StatusBaseText {
id: waitingForText
visible: !responseMessage.visible
text: qsTr("Waiting for %1's response...").arg(root.verificationResponseDisplayName)
font.pixelSize: Style.current.additionalTextSize
horizontalAlignment : Text.AlignHCenter
Layout.fillWidth: true
wrapMode: Text.WordWrap
color: Theme.palette.baseColor1
}
StatusBaseText {
Layout.fillWidth: true
Layout.topMargin: Style.current.halfPadding
text: root.hasReply ? qsTr("Still not sure?") + " " + Utils.getLinkStyle(qsTr("Ask something else"), hoveredLink, Style.current.blue)
: qsTr("Awaiting %1's response...").arg(root.verificationResponseDisplayName)
font.pixelSize: Style.current.additionalTextSize
horizontalAlignment : Text.AlignHCenter
wrapMode: Text.WordWrap
textFormat: Text.RichText
color: root.hasReply ? Theme.palette.directColor1 : Theme.palette.baseColor1
onLinkActivated: {
root.verificationRequestCanceled(root.publicKey)
root.close()
Global.openSendIDRequestPopup(root.publicKey, root.contactDetails, null)
}
}
}

View File

@ -19,12 +19,8 @@ CommonContactDialog {
readonly property var d: QtObject {
id: d
readonly property int outgoingVerificationStatus: contactDetails.verificationStatus
readonly property int incomingVerificationStatus: contactDetails.incomingVerificationStatus
readonly property bool isVerificationRequestReceived: incomingVerificationStatus === Constants.verificationStatus.verifying ||
incomingVerificationStatus === Constants.verificationStatus.verified
readonly property bool isTrusted: outgoingVerificationStatus === Constants.verificationStatus.trusted ||
incomingVerificationStatus === Constants.verificationStatus.trusted
readonly property bool isTrusted: contactDetails.outgoingVerificationStatus === Constants.verificationStatus.trusted ||
contactDetails.incomingVerificationStatus === Constants.verificationStatus.trusted
}
StatusBaseText {
@ -36,7 +32,7 @@ CommonContactDialog {
StatusCheckBox {
id: ctrlRemoveIDVerification
visible: contactDetails.isContact && !d.isTrusted && d.isVerificationRequestReceived
visible: d.isTrusted || contactDetails.trustStatus === Constants.trustStatus.trusted
checked: visible
enabled: false
text: qsTr("Remove ID verification")

View File

@ -1,5 +1,4 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15

View File

@ -1,5 +1,4 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
@ -22,7 +21,7 @@ CommonContactDialog {
signal accepted(string message)
title: qsTr("Send Contact Request")
title: qsTr("Send contact request")
onAboutToShow: {
messageInput.input.edit.forceActiveFocus()
@ -30,7 +29,7 @@ CommonContactDialog {
// (request) update from mailserver
if (d.userDisplayName === "") {
root.rootStore.contactStore.requestContactInfo(root.publicKey)
d.loadingContactDetails = true
root.loadingContactDetails = true
}
}
@ -40,18 +39,6 @@ CommonContactDialog {
readonly property int maxMsgLength: 280
readonly property int minMsgLength: 1
readonly property int msgHeight: 152
property bool loadingContactDetails: false
property var contactDetails: root.contactDetails
readonly property bool userIsEnsVerified: contactDetails.ensVerified
readonly property string userDisplayName: contactDetails.displayName
readonly property string userNickName: contactDetails.localNickname
readonly property string prettyEnsName: contactDetails.name
readonly property string aliasName: contactDetails.alias
readonly property string mainDisplayName: ProfileUtils.displayName(userNickName, prettyEnsName, userDisplayName, aliasName)
readonly property var userIcon: contactDetails.largeImage
}
readonly property var _conn: Connections {
@ -61,8 +48,8 @@ CommonContactDialog {
if (publicKey !== root.publicKey)
return
if (ok)
d.contactDetails = Utils.getContactDetailsAsJson(root.publicKey, false)
d.loadingContactDetails = false
root.contactDetails = Utils.getContactDetailsAsJson(root.publicKey, false)
root.loadingContactDetails = false
}
}

View File

@ -218,26 +218,14 @@ Pane {
}
Component {
id: txtRejectedContactRequestComponent
StatusBaseText {
font.pixelSize: 13
font.weight: Font.Medium
color: Theme.palette.baseColor1
verticalAlignment: Text.AlignVCenter
text: qsTr("Contact Request Rejected")
}
}
Component {
id: btnRespondToIdRequestComponent
StatusButton {
id: btnReplyToIdRequestComponent
StatusFlatButton {
size: StatusButton.Size.Small
text: qsTr("Respond to ID verification request")
text: qsTr("Reply to ID verification request")
objectName: "respondToIDRequest_StatusItem"
onClicked: {
Global.openIncomingIDRequestPopup(root.publicKey,
popup => popup.closed.connect(d.reload))
}
icon.name: "checkmark-circle"
onClicked: Global.openIncomingIDRequestPopup(root.publicKey, d.contactDetails,
popup => popup.closed.connect(d.reload))
}
}
@ -246,14 +234,22 @@ Pane {
StatusFlatButton {
size: StatusButton.Size.Small
text: qsTr("Request ID verification")
objectName: "requestIDVerification_StatusItem"
icon.name: "checkmark-circle"
enabled: d.isContact && !d.isBlocked && !d.isLocallyTrusted &&
d.outgoingVerificationStatus === Constants.verificationStatus.unverified &&
!d.isVerificationRequestReceived
onClicked: {
Global.openSendIDRequestPopup(root.publicKey, d.contactDetails,
popup => popup.accepted.connect(d.reload))
}
onClicked: Global.openSendIDRequestPopup(root.publicKey, d.contactDetails,
popup => popup.accepted.connect(d.reload))
}
}
Component {
id: btnReviewIDVerificationReply
StatusFlatButton {
size: StatusButton.Size.Small
text: d.incomingVerificationStatus !== Constants.verificationStatus.verified ? qsTr("ID verification pending")
: qsTr("Review ID verification reply")
icon.name: d.incomingVerificationStatus !== Constants.verificationStatus.verified ? "history" : "checkmark-circle"
onClicked: Global.openOutgoingIDRequestPopup(root.publicKey, d.contactDetails,
popup => popup.closed.connect(d.reload))
}
}
@ -314,6 +310,7 @@ Pane {
Item { Layout.fillWidth: true }
// secondary action button
Loader {
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: menuButton.visible ? menuButton.height : -1
@ -321,13 +318,18 @@ Pane {
if (d.isCurrentUser && !root.readOnly)
return btnShareProfile
if (d.isContact && !d.isBlocked && !d.isLocallyTrusted &&
d.outgoingVerificationStatus === Constants.verificationStatus.unverified &&
!d.isVerificationRequestReceived)
return btnRequestIDVerification
if (d.isContact && !(d.isTrusted || d.isLocallyTrusted) && !d.isBlocked) {
if (d.isVerificationRequestSent)
return btnReviewIDVerificationReply
else if (d.isVerificationRequestReceived)
return btnReplyToIdRequestComponent
else if (d.outgoingVerificationStatus === Constants.verificationStatus.unverified)
return btnRequestIDVerification
}
}
}
// primary action button
Loader {
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: menuButton.visible ? menuButton.height : -1
@ -365,10 +367,8 @@ Pane {
case Constants.ContactRequestState.Received:
break // handled above
case Constants.ContactRequestState.Mutual: {
if (d.incomingVerificationStatus === Constants.verificationStatus.declined) {
if (d.outgoingVerificationStatus === Constants.verificationStatus.declined) {
return btnBlockUserComponent
} else if (!d.isTrusted && d.isVerificationRequestReceived) {
return btnRespondToIdRequestComponent
}
break
}
@ -412,15 +412,6 @@ Pane {
onTriggered: Global.openMarkAsIDVerifiedPopup(root.publicKey, d.contactDetails,
popup => popup.accepted.connect(d.reload))
}
StatusAction {
text: qsTr("Review ID verification reply")
icon.name: "checkmark-circle"
enabled: d.isContact && !d.isBlocked && !d.isTrusted && d.isVerificationRequestSent
onTriggered: {
Global.openOutgoingIDRequestPopup(root.publicKey,
popup => popup.closed.connect(d.reload))
}
}
StatusAction {
text: d.userNickName ? qsTr("Edit nickname") : qsTr("Add nickname")
icon.name: "edit_pencil"
@ -468,6 +459,13 @@ Pane {
Global.markAsUntrustedRequested(root.publicKey, d.contactDetails)
}
}
StatusAction {
text: qsTr("Cancel ID verification request")
icon.name: "delete"
type: StatusAction.Type.Danger
enabled: d.isContact && !d.isBlocked && d.isVerificationRequestSent
onTriggered: root.contactsStore.cancelVerificationRequest(root.publicKey)
}
StatusAction {
text: qsTr("Remove untrusted mark")
icon.name: "warning"
@ -595,16 +593,6 @@ Pane {
width: scrollView.availableWidth
spacing: 20
// TODO own tab in Showcase
// ProfileBioSocialsPanel {
// Layout.fillWidth: true
// Layout.leftMargin: column.anchors.leftMargin + Style.current.halfPadding
// Layout.rightMargin: column.anchors.rightMargin + Style.current.halfPadding
// bio: root.dirty ? root.dirtyValues.bio : d.contactDetails.bio
// userSocialLinksJson: root.readOnly ? root.profileStore.temporarySocialLinksJson
// : d.contactDetails.socialLinks
// }
StatusTabBar {
id: showcaseTabBar
Layout.fillWidth: true

View File

@ -170,19 +170,24 @@ StatusMenu {
StatusAction {
id: pendingIdentityAction
objectName: "pendingIdentity_StatusItem"
text: isVerificationRequestSent || root.incomingVerificationStatus === Constants.verificationStatus.verified ? qsTr("ID Request Pending...")
: qsTr("Respond to ID Request...")
icon.name: "checkmark-circle"
enabled: !root.isMe && root.isContact
&& !root.isBlockedContact && !root.isTrusted
&& (root.hasActiveReceivedVerificationRequestFrom
|| root.isVerificationRequestSent)
&& !root.isBridgedAccount
text: {
if (root.isVerificationRequestSent) {
if (root.incomingVerificationStatus !== Constants.verificationStatus.verified)
return qsTr("ID verification pending...")
return qsTr("Review ID verification reply")
}
return qsTr("Reply to ID verification request")
}
icon.name: root.isVerificationRequestSent && root.incomingVerificationStatus !== Constants.verificationStatus.verified ? "history"
: "checkmark-circle"
enabled: !root.isMe && root.isContact && !root.isBridgedAccount && !root.isBlockedContact && !(root.isTrusted || root.userIsLocallyTrusted) &&
(root.hasActiveReceivedVerificationRequestFrom || root.isVerificationRequestSent)
onTriggered: {
if (hasActiveReceivedVerificationRequestFrom) {
Global.openIncomingIDRequestPopup(root.selectedUserPublicKey, null)
if (root.hasActiveReceivedVerificationRequestFrom) {
Global.openIncomingIDRequestPopup(root.selectedUserPublicKey, root.contactDetails, null)
} else if (root.isVerificationRequestSent) {
Global.openOutgoingIDRequestPopup(root.selectedUserPublicKey, null)
Global.openOutgoingIDRequestPopup(root.selectedUserPublicKey, root.contactDetails, null)
}
root.close()
@ -199,9 +204,7 @@ StatusMenu {
}
StatusMenuSeparator {
visible: blockMenuItem.enabled
|| markUntrustworthyMenuItem.enabled
|| removeUntrustworthyMarkMenuItem.enabled
visible: blockMenuItem.enabled || unblockAction.enabled
}
StatusAction {
@ -241,6 +244,14 @@ StatusMenu {
onTriggered: Global.markAsUntrustedRequested(root.selectedUserPublicKey, root.contactDetails)
}
StatusAction {
text: qsTr("Cancel ID verification request")
icon.name: "delete"
type: StatusAction.Type.Danger
enabled: !root.isMe && root.isContact && !root.isBlockedContact && !root.isBridgedAccount && root.isVerificationRequestSent
onTriggered: root.store.contactsStore.cancelVerificationRequest(root.selectedUserPublicKey)
}
StatusAction {
id: removeUntrustworthyMarkMenuItem
objectName: "removeUntrustworthy_StatusItem"

View File

@ -45,8 +45,8 @@ QtObject {
signal markAsUntrustedRequested(string publicKey, var contactDetails)
signal removeContactRequested(string publicKey, var contactDetails)
signal openInviteFriendsToCommunityPopup(var community, var communitySectionModule, var cb)
signal openIncomingIDRequestPopup(string publicKey, var cb)
signal openOutgoingIDRequestPopup(string publicKey, var cb)
signal openIncomingIDRequestPopup(string publicKey, var contactDetails, var cb)
signal openOutgoingIDRequestPopup(string publicKey, var contactDetails, var cb)
signal openDeleteMessagePopup(string messageId, var messageStore)
signal openDownloadImageDialog(string imageSource)
signal openExportControlNodePopup(var community)