diff --git a/storybook/pages/ProfileDialogViewPage.qml b/storybook/pages/ProfileDialogViewPage.qml index e32ff614e83..ca526609662 100644 --- a/storybook/pages/ProfileDialogViewPage.qml +++ b/storybook/pages/ProfileDialogViewPage.qml @@ -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%! What’s 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%! What’s 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,6 +479,17 @@ SplitView { { value: Constants.ContactRequestState.Dismissed, text: "Dismissed" } ] } + Label { text: "trustStatus:" } + ComboBox { + id: ctrlTrustStatus + textRole: "text" + valueRole: "value" + model: [ + { value: Constants.trustStatus.unknown, text: "unknown" }, + { value: Constants.trustStatus.trusted, text: "trusted" }, + { value: Constants.trustStatus.untrustworthy, text: "untrustworthy" } + ] + } CheckBox { id: ctrlIsBlocked text: "isBlocked" @@ -399,29 +497,71 @@ SplitView { } RowLayout { Layout.fillWidth: true - enabled: !switchOwnProfile.checked - Label { text: "trustStatus:" } + + Label { text: "incomingVerificationStatus:" } ComboBox { - id: ctrlTrustStatus + id: ctrlIncomingVerificationStatus + enabled: ctrlIsContact.checked && !switchOwnProfile.checked textRole: "text" valueRole: "value" model: [ - { value: Constants.trustStatus.unknown, text: "unknown" }, - { value: Constants.trustStatus.trusted, text: "trusted" }, - { value: Constants.trustStatus.untrustworthy, text: "untrustworthy" } + { 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: "onlineStatus" } + Label { text: "verificationStatus:" } ComboBox { - id: ctrlOnlineStatus + id: ctrlVerificationStatus + 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" } ] } + 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 diff --git a/ui/app/AppLayouts/Profile/panels/ContactPanel.qml b/ui/app/AppLayouts/Profile/panels/ContactPanel.qml index 49ad1743ecc..25c1b8ddfc7 100644 --- a/ui/app/AppLayouts/Profile/panels/ContactPanel.qml +++ b/ui/app/AppLayouts/Profile/panels/ContactPanel.qml @@ -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) }, diff --git a/ui/app/AppLayouts/Profile/popups/ConfirmChangePasswordModal.qml b/ui/app/AppLayouts/Profile/popups/ConfirmChangePasswordModal.qml index 51e31e1d3c9..4c7bd94a7c4 100644 --- a/ui/app/AppLayouts/Profile/popups/ConfirmChangePasswordModal.qml +++ b/ui/app/AppLayouts/Profile/popups/ConfirmChangePasswordModal.qml @@ -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" diff --git a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml index a5da3055740..61fd92ccdd7 100644 --- a/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml +++ b/ui/app/AppLayouts/Profile/stores/ProfileSectionStore.qml @@ -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"}) diff --git a/ui/app/AppLayouts/Profile/views/ContactsView.qml b/ui/app/AppLayouts/Profile/views/ContactsView.qml index 3028444a5e5..73f18724cfa 100644 --- a/ui/app/AppLayouts/Profile/views/ContactsView.qml +++ b/ui/app/AppLayouts/Profile/views/ContactsView.qml @@ -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 { } } } - diff --git a/ui/app/mainui/Popups.qml b/ui/app/mainui/Popups.qml index 1ce2b3411ee..4480efe3e4e 100644 --- a/ui/app/mainui/Popups.qml +++ b/ui/app/mainui/Popups.qml @@ -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)) diff --git a/ui/app/mainui/activitycenter/panels/IncomingContactVerificationCta.qml b/ui/app/mainui/activitycenter/panels/IncomingContactVerificationCta.qml index 8bcb2bad576..8515f156a33 100644 --- a/ui/app/mainui/activitycenter/panels/IncomingContactVerificationCta.qml +++ b/ui/app/mainui/activitycenter/panels/IncomingContactVerificationCta.qml @@ -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() -} \ No newline at end of file +} diff --git a/ui/app/mainui/activitycenter/views/ActivityNotificationContactVerification.qml b/ui/app/mainui/activitycenter/views/ActivityNotificationContactVerification.qml index 7428760c7e8..54029f55074 100644 --- a/ui/app/mainui/activitycenter/views/ActivityNotificationContactVerification.qml +++ b/ui/app/mainui/activitycenter/views/ActivityNotificationContactVerification.qml @@ -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() } } } -} \ No newline at end of file +} diff --git a/ui/imports/shared/popups/BlockContactConfirmationDialog.qml b/ui/imports/shared/popups/BlockContactConfirmationDialog.qml index 2b09a81b65d..aa6e3c18e58 100644 --- a/ui/imports/shared/popups/BlockContactConfirmationDialog.qml +++ b/ui/imports/shared/popups/BlockContactConfirmationDialog.qml @@ -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") diff --git a/ui/imports/shared/popups/CommonContactDialog.qml b/ui/imports/shared/popups/CommonContactDialog.qml index 30a86ccf23e..bdbb4a1ff6e 100644 --- a/ui/imports/shared/popups/CommonContactDialog.qml +++ b/ui/imports/shared/popups/CommonContactDialog.qml @@ -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 { diff --git a/ui/imports/shared/popups/ContactVerificationRequestPopup.qml b/ui/imports/shared/popups/ContactVerificationRequestPopup.qml index b702f7bc6ef..b259ea4c628 100644 --- a/ui/imports/shared/popups/ContactVerificationRequestPopup.qml +++ b/ui/imports/shared/popups/ContactVerificationRequestPopup.qml @@ -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(); - } - } - 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 = "" - } + 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 { - 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() } } } diff --git a/ui/imports/shared/popups/MarkAsIDVerifiedDialog.qml b/ui/imports/shared/popups/MarkAsIDVerifiedDialog.qml index efcfcaabad7..efb2d1316d5 100644 --- a/ui/imports/shared/popups/MarkAsIDVerifiedDialog.qml +++ b/ui/imports/shared/popups/MarkAsIDVerifiedDialog.qml @@ -1,5 +1,4 @@ import QtQuick 2.15 -import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtQml.Models 2.15 diff --git a/ui/imports/shared/popups/MarkAsUntrustedPopup.qml b/ui/imports/shared/popups/MarkAsUntrustedPopup.qml index fff13c03cb7..3f776b9f491 100644 --- a/ui/imports/shared/popups/MarkAsUntrustedPopup.qml +++ b/ui/imports/shared/popups/MarkAsUntrustedPopup.qml @@ -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") diff --git a/ui/imports/shared/popups/OutgoingContactVerificationRequestPopup.qml b/ui/imports/shared/popups/OutgoingContactVerificationRequestPopup.qml index b3435f56c4f..5cbbf6776f3 100644 --- a/ui/imports/shared/popups/OutgoingContactVerificationRequestPopup.qml +++ b/ui/imports/shared/popups/OutgoingContactVerificationRequestPopup.qml @@ -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 - - contentWidth: availableWidth - implicitWidth: 560 - - 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: 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 + } - 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 - } + 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 + } - 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) } } } diff --git a/ui/imports/shared/popups/RemoveContactPopup.qml b/ui/imports/shared/popups/RemoveContactPopup.qml index 49a69ab112c..ad0dde2201e 100644 --- a/ui/imports/shared/popups/RemoveContactPopup.qml +++ b/ui/imports/shared/popups/RemoveContactPopup.qml @@ -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") diff --git a/ui/imports/shared/popups/RemoveIDVerificationDialog.qml b/ui/imports/shared/popups/RemoveIDVerificationDialog.qml index d0c982bb91a..ff5e39d7994 100644 --- a/ui/imports/shared/popups/RemoveIDVerificationDialog.qml +++ b/ui/imports/shared/popups/RemoveIDVerificationDialog.qml @@ -1,5 +1,4 @@ import QtQuick 2.15 -import QtQuick.Controls 2.15 import QtQuick.Layouts 1.15 import QtQml.Models 2.15 diff --git a/ui/imports/shared/popups/SendContactRequestModal.qml b/ui/imports/shared/popups/SendContactRequestModal.qml index 85b1b54a2b9..974d590ee39 100644 --- a/ui/imports/shared/popups/SendContactRequestModal.qml +++ b/ui/imports/shared/popups/SendContactRequestModal.qml @@ -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 } } diff --git a/ui/imports/shared/views/ProfileDialogView.qml b/ui/imports/shared/views/ProfileDialogView.qml index 10490287d26..48646d81a24 100644 --- a/ui/imports/shared/views/ProfileDialogView.qml +++ b/ui/imports/shared/views/ProfileDialogView.qml @@ -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 diff --git a/ui/imports/shared/views/chat/ProfileContextMenu.qml b/ui/imports/shared/views/chat/ProfileContextMenu.qml index f2ef3e0dc62..fb00d070bfb 100644 --- a/ui/imports/shared/views/chat/ProfileContextMenu.qml +++ b/ui/imports/shared/views/chat/ProfileContextMenu.qml @@ -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" diff --git a/ui/imports/utils/Global.qml b/ui/imports/utils/Global.qml index 5447fb64554..30c0e07dddd 100644 --- a/ui/imports/utils/Global.qml +++ b/ui/imports/utils/Global.qml @@ -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)