feat(Profile flow) Mark as ID verified / remove verification

- implement two new actions and popups to directly mark a contact as "ID
verified" and remove the verification thereof respectively
- do not hardcode the secondary button inside ProfileDialogView and make
it a Loader
- make the CommonContactDialog contents scrollable

Fixes #13711
This commit is contained in:
Lukáš Tinkl 2024-02-26 10:39:08 +01:00 committed by Lukáš Tinkl
parent d22ac0dfed
commit 4d3745d4b2
10 changed files with 222 additions and 41 deletions

View File

@ -149,6 +149,16 @@ SplitView {
logs.logEvent("rootStore::contactStore::removeContact", ["publicKey"], arguments)
ctrlContactRequestState.currentIndex = ctrlContactRequestState.indexOfValue(Constants.ContactRequestState.None)
}
function verifiedTrusted(publicKey) {
logs.logEvent("rootStore::contactStore::verifiedTrusted", ["publicKey"], arguments)
ctrlTrustStatus.currentIndex = ctrlTrustStatus.indexOfValue(Constants.trustStatus.trusted)
}
function removeTrustStatus(publicKey) {
logs.logEvent("rootStore::contactStore::removeTrustStatus", ["publicKey"], arguments)
ctrlTrustStatus.currentIndex = ctrlTrustStatus.indexOfValue(Constants.trustStatus.unknown)
}
}
}
communityTokensStore: CommunityTokensStore {}
@ -244,6 +254,7 @@ SplitView {
function verifiedTrusted(publicKey) {
logs.logEvent("contactsStore::verifiedTrusted", ["publicKey"], arguments)
ctrlTrustStatus.currentIndex = ctrlTrustStatus.indexOfValue(Constants.trustStatus.trusted)
}
function getLinkToProfile(publicKey) {

View File

@ -102,7 +102,7 @@ Button {
case StatusBaseButton.Size.Tiny:
return 5
case StatusBaseButton.Size.Small:
return 10
return 8
case StatusBaseButton.Size.Large:
default:
return 11

View File

@ -47,6 +47,8 @@ QtObject {
Component.onCompleted: {
Global.openSendIDRequestPopup.connect(openSendIDRequestPopup)
Global.openMarkAsIDVerifiedPopup.connect(openMarkAsIDVerifiedPopup)
Global.openRemoveIDVerificationDialog.connect(openRemoveIDVerificationDialog)
Global.openOutgoingIDRequestPopup.connect(openOutgoingIDRequestPopup)
Global.openIncomingIDRequestPopup.connect(openIncomingIDRequestPopup)
Global.openInviteFriendsToCommunityPopup.connect(openInviteFriendsToCommunityPopup)
@ -173,6 +175,14 @@ QtObject {
}, cb)
}
function openMarkAsIDVerifiedPopup(publicKey, contactDetails, cb) {
openPopup(markAsIDVerifiedPopupComponent, {publicKey, contactDetails}, cb)
}
function openRemoveIDVerificationDialog(publicKey, contactDetails, cb) {
openPopup(removeIDVerificationPopupComponent, {publicKey, contactDetails}, cb)
}
function openOutgoingIDRequestPopup(publicKey, cb) {
try {
const verificationDetails = root.rootStore.profileSectionStore.contactsStore.getSentVerificationDetailsAsJson(publicKey)
@ -397,6 +407,43 @@ QtObject {
}
},
Component {
id: markAsIDVerifiedPopupComponent
MarkAsIDVerifiedDialog {
onAccepted: {
rootStore.contactStore.verifiedTrusted(publicKey)
Global.displaySuccessToastMessage(qsTr("%1 ID verified").arg(mainDisplayName))
close()
}
onClosed: destroy()
}
},
Component {
id: removeIDVerificationPopupComponent
RemoveIDVerificationDialog {
onAccepted: {
rootStore.contactStore.removeTrustStatus(publicKey)
if (markAsUntrusted && removeContact) {
rootStore.contactStore.markUntrustworthy(publicKey)
rootStore.contactStore.removeContact(publicKey)
Global.displaySuccessToastMessage(qsTr("%1 ID verification removed, removed from contacts and marked as untrusted").arg(mainDisplayName))
} else if (markAsUntrusted) {
rootStore.contactStore.markUntrustworthy(publicKey)
Global.displaySuccessToastMessage(qsTr("%1 ID verification removed and marked as untrusted").arg(mainDisplayName))
} else if (removeContact) {
rootStore.contactStore.removeContact(publicKey)
Global.displaySuccessToastMessage(qsTr("%1 ID verification removed and removed from contacts").arg(mainDisplayName))
} else {
Global.displaySuccessToastMessage(qsTr("%1 ID verification removed").arg(mainDisplayName))
}
close()
}
onClosed: destroy()
}
},
Component {
id: inviteFriendsToCommunityPopup

View File

@ -27,12 +27,15 @@ StatusDialog {
readonly property string optionalDisplayName: ProfileUtils.displayName("", contactDetails.name, contactDetails.displayName, contactDetails.alias)
width: 480
horizontalPadding: 16
verticalPadding: 20
horizontalPadding: 0
topPadding: 20
bottomPadding: 0
contentItem: ColumnLayout {
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
spacing: Style.current.padding
UserImage {
@ -114,12 +117,21 @@ StatusDialog {
StatusDialogDivider {
Layout.fillWidth: true
Layout.topMargin: 15
Layout.bottomMargin: 15
Layout.topMargin: Style.current.padding
Layout.leftMargin: Style.current.padding
Layout.rightMargin: Style.current.padding
}
ColumnLayout {
id: contentLayout
StatusScrollView {
Layout.fillWidth: true
Layout.fillHeight: true
contentWidth: availableWidth
id: scrollView
ColumnLayout {
width: scrollView.availableWidth
id: contentLayout
}
}
}

View File

@ -0,0 +1,32 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
import utils 1.0
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
CommonContactDialog {
id: root
title: qsTr("Mark as ID verified")
StatusBaseText {
Layout.fillWidth: true
wrapMode: Text.WordWrap
text: qsTr("Mark users as ID verified only if youre 100% sure who they are. Otherwise, its safer to send %1 an ID verification request.").arg(mainDisplayName)
}
rightButtons: ObjectModel {
StatusFlatButton {
text: qsTr("Cancel")
onClicked: root.close()
}
StatusButton {
text: qsTr("Mark as ID verified")
onClicked: root.accepted()
}
}
}

View File

@ -0,0 +1,47 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQml.Models 2.15
import utils 1.0
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
CommonContactDialog {
id: root
readonly property bool markAsUntrusted: ctrlMarkAsUntrusted.checked
readonly property bool removeContact: ctrlRemoveContact.checked
title: qsTr("Remove ID verification")
StatusBaseText {
Layout.fillWidth: true
Layout.bottomMargin: Style.current.halfPadding
wrapMode: Text.WordWrap
text: qsTr("%1s identity will no longer be verified. This is only visible to you.").arg(mainDisplayName)
}
StatusCheckBox {
id: ctrlMarkAsUntrusted
text: qsTr("Mark %1 as untrusted").arg(mainDisplayName)
}
StatusCheckBox {
id: ctrlRemoveContact
text: qsTr("Remove contact")
}
rightButtons: ObjectModel {
StatusFlatButton {
text: qsTr("Cancel")
onClicked: root.close()
}
StatusButton {
type: StatusBaseButton.Type.Danger
text: qsTr("Remove ID verification")
onClicked: root.accepted()
}
}
}

View File

@ -29,3 +29,5 @@ ConfirmExternalLinkPopup 1.0 ConfirmExternalLinkPopup.qml
CommunityAssetsInfoPopup 1.0 CommunityAssetsInfoPopup.qml
MarkAsUntrustedPopup 1.0 MarkAsUntrustedPopup.qml
RemoveContactPopup 1.0 RemoveContactPopup.qml
MarkAsIDVerifiedDialog 1.0 MarkAsIDVerifiedDialog.qml
RemoveIDVerificationDialog 1.0 RemoveIDVerificationDialog.qml

View File

@ -80,6 +80,7 @@ Pane {
readonly property bool isTrusted: outgoingVerificationStatus === Constants.verificationStatus.trusted ||
incomingVerificationStatus === Constants.verificationStatus.trusted
readonly property bool isLocallyTrusted: contactDetails.trustStatus === Constants.trustStatus.trusted
readonly property string linkToProfile: root.contactsStore.getLinkToProfile(root.publicKey)
@ -240,14 +241,28 @@ Pane {
}
}
ConfirmationDialog {
id: removeVerificationConfirmationDialog
headerSettings.title: qsTr("Remove contact verification")
confirmationText: qsTr("This will remove the contact's verified status. Please confirm.")
onConfirmButtonClicked: {
root.contactsStore.removeTrustStatus(root.publicKey)
close()
d.reload()
Component {
id: btnRequestIDVerification
StatusFlatButton {
size: StatusButton.Size.Small
text: qsTr("Request ID verification")
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))
}
}
}
Component {
id: btnShareProfile
StatusFlatButton {
size: StatusButton.Size.Small
text: qsTr("Share Profile")
onClicked: Global.openPopup(shareProfileCmp)
}
}
@ -299,14 +314,18 @@ Pane {
Item { Layout.fillWidth: true }
// TODO a Loader with additional secondary buttons
StatusFlatButton {
Loader {
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: menuButton.visible ? menuButton.height : -1
visible: d.isCurrentUser && !root.readOnly
size: StatusButton.Size.Small
text: qsTr("Share Profile")
onClicked: Global.openPopup(shareProfileCmp)
sourceComponent: {
if (d.isCurrentUser && !root.readOnly)
return btnShareProfile
if (d.isContact && !d.isBlocked && !d.isLocallyTrusted &&
d.outgoingVerificationStatus === Constants.verificationStatus.unverified &&
!d.isVerificationRequestReceived)
return btnRequestIDVerification
}
}
Loader {
@ -387,17 +406,12 @@ Pane {
}
}
StatusAction {
text: qsTr("Request ID verification")
text: qsTr("Mark as ID verified")
icon.name: "checkmark-circle"
enabled: d.isContact && !d.isBlocked &&
d.outgoingVerificationStatus === Constants.verificationStatus.unverified &&
!d.isVerificationRequestReceived
onTriggered: {
Global.openSendIDRequestPopup(root.publicKey, d.contactDetails,
popup => popup.accepted.connect(d.reload))
}
enabled: d.isContact && !d.isBlocked && !(d.isTrusted || d.isLocallyTrusted)
onTriggered: Global.openMarkAsIDVerifiedPopup(root.publicKey, d.contactDetails,
popup => popup.accepted.connect(d.reload))
}
// TODO Mark as ID verified
StatusAction {
text: qsTr("Review ID verification reply")
icon.name: "checkmark-circle"
@ -432,12 +446,11 @@ Pane {
StatusMenuSeparator {}
StatusAction {
text: qsTr("Remove ID verification")
icon.name: "warning"
icon.name: "delete"
type: StatusAction.Type.Danger
enabled: d.isContact && d.isTrusted
onTriggered: {
removeVerificationConfirmationDialog.open()
}
enabled: d.isContact && (d.isTrusted || d.isLocallyTrusted)
onTriggered: Global.openRemoveIDVerificationDialog(root.publicKey, d.contactDetails,
popup => popup.closed.connect(d.reload))
}
StatusAction {
text: qsTr("Remove nickname")

View File

@ -79,6 +79,7 @@ StatusMenu {
readonly property bool userTrustIsUnknown: contactDetails && contactDetails.trustStatus === Constants.trustStatus.unknown
readonly property bool userIsUntrustworthy: contactDetails && contactDetails.trustStatus === Constants.trustStatus.untrustworthy
readonly property bool userIsLocallyTrusted: contactDetails && contactDetails.trustStatus === Constants.trustStatus.trusted
signal openProfileClicked(string publicKey)
signal createOneToOneChat(string communityId, string chatId, string ensName)
@ -152,13 +153,20 @@ StatusMenu {
objectName: "verifyIdentity_StatusItem"
icon.name: "checkmark-circle"
enabled: !root.isMe && root.isContact
&& !root.isBlockedContact
&& root.outgoingVerificationStatus === Constants.verificationStatus.unverified
&& !root.hasActiveReceivedVerificationRequestFrom
&& !root.isBridgedAccount
&& !root.isBlockedContact
&& !root.userIsLocallyTrusted
&& root.outgoingVerificationStatus === Constants.verificationStatus.unverified
&& !root.hasActiveReceivedVerificationRequestFrom
&& !root.isBridgedAccount
onTriggered: Global.openSendIDRequestPopup(root.selectedUserPublicKey, root.contactDetails, null)
}
// TODO Mark as ID verified
StatusAction {
text: qsTr("Mark as ID verified")
objectName: "markAsVerified_StatusItem"
icon.name: "checkmark-circle"
enabled: !root.isMe && root.isContact && !root.isBridgedAccount && !root.isBlockedContact && !(root.isTrusted || root.userIsLocallyTrusted)
onTriggered: Global.openMarkAsIDVerifiedPopup(root.selectedUserPublicKey, root.contactDetails, null)
}
StatusAction {
id: pendingIdentityAction
objectName: "pendingIdentity_StatusItem"
@ -214,7 +222,14 @@ StatusMenu {
onTriggered: Global.unblockContactRequested(root.selectedUserPublicKey, root.contactDetails)
}
// TODO Remove ID verification + confirmation dialog
StatusAction {
objectName: "removeIDVerification_StatusItem"
text: qsTr("Remove ID verification")
icon.name: "delete"
type: StatusAction.Type.Danger
enabled: !root.isMe && root.isContact && !root.isBridgedAccount && (root.isTrusted || root.userIsLocallyTrusted)
onTriggered: Global.openRemoveIDVerificationDialog(root.selectedUserPublicKey, root.contactDetails, null)
}
StatusAction {
id: markUntrustworthyMenuItem

View File

@ -39,6 +39,8 @@ QtObject {
signal openProfilePopupRequested(string publicKey, var parentPopup, var cb)
signal openActivityCenterPopupRequested()
signal openSendIDRequestPopup(string publicKey, var contactDetails, var cb)
signal openMarkAsIDVerifiedPopup(string publicKey, var contactDetails, var cb)
signal openRemoveIDVerificationDialog(string publicKey, var contactDetails, var cb)
signal openContactRequestPopup(string publicKey, var contactDetails, var cb)
signal markAsUntrustedRequested(string publicKey, var contactDetails)
signal removeContactRequested(string publicKey, var contactDetails)