fix(ActivityCenter): Add identity verification AC notifications

This commit is contained in:
MishkaRogachev 2022-11-15 13:11:55 +03:00 committed by Mikhail Rogachev
parent e1f60c9ed0
commit f70ccfc17d
17 changed files with 262 additions and 13 deletions

View File

@ -2,6 +2,7 @@ import strformat, stint
import ../../shared_models/message_item_qobject
import ../../../../app_service/service/activity_center/dto/notification
import ../../../../app_service/service/chat/dto/chat
import ../../../../app_service/service/contacts/dto/contacts
const CONTACT_REQUEST_PENDING_STATE = 1
@ -10,6 +11,7 @@ type Item* = ref object
chatId: string
communityId: string
membershipStatus: ActivityCenterMembershipStatus
verificationStatus: VerificationStatus
sectionId: string
name: string
author: string
@ -27,6 +29,7 @@ proc initItem*(
chatId: string,
communityId: string,
membershipStatus: ActivityCenterMembershipStatus,
verificationStatus: VerificationStatus,
sectionId: string,
name: string,
author: string,
@ -44,6 +47,7 @@ proc initItem*(
result.chatId = chatId
result.communityId = communityId
result.membershipStatus = membershipStatus
result.verificationStatus = verificationStatus
result.sectionId = sectionId
result.name = name
result.author = author
@ -63,6 +67,7 @@ proc `$`*(self: Item): string =
chatId: {$self.chatId},
communityId: {$self.communityId},
membershipStatus: {$self.membershipStatus.int},
verificationStatus: {$self.verificationStatus.int},
sectionId: {$self.sectionId},
author: {$self.author},
notificationType: {$self.notificationType.int},
@ -86,7 +91,6 @@ proc author*(self: Item): string =
proc chatId*(self: Item): string =
return self.chatId
proc chatType*(self: Item): ChatType =
return self.chatType
@ -96,6 +100,9 @@ proc communityId*(self: Item): string =
proc membershipStatus*(self: Item): ActivityCenterMembershipStatus =
return self.membershipStatus
proc verificationStatus*(self: Item): VerificationStatus =
return self.verificationStatus
proc sectionId*(self: Item): string =
return self.sectionId
@ -129,6 +136,9 @@ proc isNotReadOrActiveCTA*(self: Item): bool =
self.membershipStatus == ActivityCenterMembershipStatus.Pending) or
(self.notificationType == ActivityCenterNotificationType.ContactRequest and
self.messageItem.contactRequestState == CONTACT_REQUEST_PENDING_STATE) or
(self.notificationType == ActivityCenterNotificationType.ContactVerification and
(self.verificationStatus == VerificationStatus.Verifying or
self.verificationStatus == VerificationStatus.Verified)) or
(self.notificationType == ActivityCenterNotificationType.Mention and
not self.read) or
(self.notificationType == ActivityCenterNotificationType.Reply and

View File

@ -7,6 +7,7 @@ type
ChatId
CommunityId
MembershipStatus
VerificationStatus
SectionId
Name
NotificationType
@ -79,6 +80,7 @@ QtObject:
of NotifRoles.ChatId: result = newQVariant(activityNotificationItem.chatId)
of NotifRoles.CommunityId: result = newQVariant(activityNotificationItem.communityId)
of NotifRoles.MembershipStatus: result = newQVariant(activityNotificationItem.membershipStatus.int)
of NotifRoles.VerificationStatus: result = newQVariant(activityNotificationItem.verificationStatus.int)
of NotifRoles.SectionId: result = newQVariant(activityNotificationItem.sectionId)
of NotifRoles.Name: result = newQVariant(activityNotificationItem.name)
of NotifRoles.Author: result = newQVariant(activityNotificationItem.author)
@ -101,6 +103,7 @@ QtObject:
NotifRoles.ChatId.int:"chatId",
NotifRoles.CommunityId.int:"communityId",
NotifRoles.MembershipStatus.int: "membershipStatus",
NotifRoles.VerificationStatus.int: "verificationStatus",
NotifRoles.SectionId.int: "sectionId",
NotifRoles.Name.int: "name",
NotifRoles.Author.int: "author",

View File

@ -126,11 +126,15 @@ method convertToItems*(
let repliedMessage = self.controller.getMessageById(n.chatId, n.message.responseTo)
repliedMessageItem = self.createMessageItemFromDto(repliedMessage, chatDetails)
if (n.notificationType == ActivityCenterNotificationType.ContactVerification):
repliedMessageItem = self.createMessageItemFromDto(n.replyMessage, chatDetails)
return notification_item.initItem(
n.id,
n.chatId,
n.communityId,
n.membershipStatus,
n.verificationStatus,
sectionId,
n.name,
n.author,

View File

@ -16,6 +16,7 @@ type
Declined
Canceled
Trusted
Untrustworthy
type
UserItem* = ref object of RootObj

View File

@ -2,6 +2,7 @@
import json, strformat, strutils, stint, json_serialization
import ../../message/dto/message
import ../../contacts/dto/contacts
include ../../../common/json_utils
include ../../../common/utils
@ -30,10 +31,12 @@ type ActivityCenterNotificationDto* = ref object of RootObj
chatId*: string
communityId*: string
membershipStatus*: ActivityCenterMembershipStatus
verificationStatus*: VerificationStatus
name*: string
author*: string
notificationType*: ActivityCenterNotificationType
message*: MessageDto
replyMessage*: MessageDto
timestamp*: int64
read*: bool
dismissed*: bool
@ -45,6 +48,7 @@ proc `$`*(self: ActivityCenterNotificationDto): string =
chatId: {self.chatId},
communityId: {self.communityId},
membershipStatus: {self.membershipStatus},
contactVerificationStatus: {self.verificationStatus},
author: {self.author},
notificationType: {$self.notificationType.int},
timestamp: {self.timestamp},
@ -52,6 +56,7 @@ proc `$`*(self: ActivityCenterNotificationDto): string =
dismissed: {$self.dismissed},
accepted: {$self.accepted},
message: {self.message}
replyMessage: {self.replyMessage}
)"""
proc toActivityCenterNotificationDto*(jsonObj: JsonNode): ActivityCenterNotificationDto =
@ -67,6 +72,13 @@ proc toActivityCenterNotificationDto*(jsonObj: JsonNode): ActivityCenterNotifica
membershipStatusInt <= ord(high(ActivityCenterMembershipStatus)))):
result.membershipStatus = ActivityCenterMembershipStatus(membershipStatusInt)
result.verificationStatus = VerificationStatus.Unverified
var verificationStatusInt: int
if (jsonObj.getProp("contactVerificationStatus", verificationStatusInt) and
(verificationStatusInt >= ord(low(VerificationStatus)) or
verificationStatusInt <= ord(high(VerificationStatus)))):
result.verificationStatus = VerificationStatus(verificationStatusInt)
discard jsonObj.getProp("author", result.author)
result.notificationType = ActivityCenterNotificationType.Unknown
@ -87,6 +99,9 @@ proc toActivityCenterNotificationDto*(jsonObj: JsonNode): ActivityCenterNotifica
jsonObj.contains("lastMessage") and jsonObj{"lastMessage"}.kind != JNull:
result.message = jsonObj{"lastMessage"}.toMessageDto()
if jsonObj.contains("replyMessage") and jsonObj{"replyMessage"}.kind != JNull:
result.replyMessage = jsonObj{"replyMessage"}.toMessageDto()
proc parseActivityCenterNotifications*(rpcResult: JsonNode): (string, seq[ActivityCenterNotificationDto]) =
var notifs: seq[ActivityCenterNotificationDto] = @[]

View File

@ -30,6 +30,7 @@ type VerificationStatus* {.pure.}= enum
Declined = 3
Canceled = 4
Trusted = 5
Untrustworthy = 6
type VerificationRequest* = object
id*: string

View File

@ -592,6 +592,7 @@ QtObject:
if not response.error.isNil:
let msg = response.error.message
raise newException(RpcException, msg)
self.activityCenterService.parseACNotificationResponse(response)
if self.contacts.hasKey(publicKey):
self.contacts[publicKey].trustStatus = TrustStatus.Trusted
@ -616,10 +617,11 @@ QtObject:
if not response.error.isNil:
let msg = response.error.message
raise newException(RpcException, msg)
self.activityCenterService.parseACNotificationResponse(response)
if self.contacts.hasKey(publicKey):
self.contacts[publicKey].trustStatus = TrustStatus.Untrustworthy
self.contacts[publicKey].verificationStatus = VerificationStatus.Verified
self.contacts[publicKey].verificationStatus = VerificationStatus.Untrustworthy
self.events.emit(SIGNAL_CONTACT_UNTRUSTWORTHY,
TrustArgs(publicKey: publicKey, isUntrustworthy: true))
@ -690,6 +692,7 @@ QtObject:
self.saveContact(contact)
self.events.emit(SIGNAL_CONTACT_VERIFICATION_SENT, ContactArgs(contactId: publicKey))
self.activityCenterService.parseACNotificationResponse(response)
except Exception as e:
error "Error sending verification request", msg = e.msg
@ -713,6 +716,7 @@ QtObject:
self.saveContact(contact)
self.events.emit(SIGNAL_CONTACT_VERIFICATION_CANCELLED, ContactArgs(contactId: publicKey))
self.activityCenterService.parseACNotificationResponse(response)
except Exception as e:
error "Error canceling verification request", msg = e.msg
@ -734,6 +738,7 @@ QtObject:
self.events.emit(SIGNAL_CONTACT_VERIFICATION_ACCEPTED,
VerificationRequestArgs(verificationRequest: request))
self.activityCenterService.parseACNotificationResponse(response)
except Exception as e:
error "error accepting contact verification request", msg=e.msg
@ -752,5 +757,6 @@ QtObject:
self.receivedIdentityRequests[publicKey] = request
self.events.emit(SIGNAL_CONTACT_VERIFICATION_DECLINED, ContactArgs(contactId: publicKey))
self.activityCenterService.parseACNotificationResponse(response)
except Exception as e:
error "error declining contact verification request", msg=e.msg

View File

@ -18,6 +18,7 @@ Item {
property bool hasMentions: false
property bool hasReplies: false
property bool hasContactRequests: false
property bool hasIdentityRequests: false
property bool hasMembership: false
property bool hideReadNotifications: false
@ -49,14 +50,14 @@ Item {
Repeater {
// NOTE: some entries are hidden until implimentation
model: [ { text: qsTr("All"), category: ActivityCenterPopup.ActivityCategory.All, visible: true, enabled: true },
{ text: qsTr("Admin"), category: ActivityCenterPopup.ActivityCategory.Admin, visible: root.hasAdmin, enabled: root.hasAdmin },
{ text: qsTr("Mentions"), category: ActivityCenterPopup.ActivityCategory.Mentions, visible: true, enabled: root.hasMentions },
{ text: qsTr("Replies"), category: ActivityCenterPopup.ActivityCategory.Replies, visible: true, enabled: root.hasReplies },
{ text: qsTr("Contact requests"), category: ActivityCenterPopup.ActivityCategory.ContactRequests, visible: true, enabled: root.hasContactRequests },
{ text: qsTr("Identity verification"), category: ActivityCenterPopup.ActivityCategory.IdentityVerification, visible: false, enabled: true },
{ text: qsTr("Transactions"), category: ActivityCenterPopup.ActivityCategory.Transactions, visible: false, enabled: true },
{ text: qsTr("Membership"), category: ActivityCenterPopup.ActivityCategory.Membership, visible: true, enabled: root.hasMembership },
{ text: qsTr("System"), category: ActivityCenterPopup.ActivityCategory.System, visible: false, enabled: true } ]
{ text: qsTr("Admin"), category: ActivityCenterPopup.ActivityCategory.Admin, visible: root.hasAdmin, enabled: root.hasAdmin },
{ text: qsTr("Mentions"), category: ActivityCenterPopup.ActivityCategory.Mentions, visible: true, enabled: root.hasMentions },
{ text: qsTr("Replies"), category: ActivityCenterPopup.ActivityCategory.Replies, visible: true, enabled: root.hasReplies },
{ text: qsTr("Contact requests"), category: ActivityCenterPopup.ActivityCategory.ContactRequests, visible: true, enabled: root.hasContactRequests },
{ text: qsTr("Identity verification"), category: ActivityCenterPopup.ActivityCategory.IdentityVerification, visible: true, enabled: root.hasIdentityRequests },
{ text: qsTr("Transactions"), category: ActivityCenterPopup.ActivityCategory.Transactions, visible: false, enabled: true },
{ text: qsTr("Membership"), category: ActivityCenterPopup.ActivityCategory.Membership, visible: true, enabled: root.hasMembership },
{ text: qsTr("System"), category: ActivityCenterPopup.ActivityCategory.System, visible: false, enabled: true } ]
StatusFlatButton {
enabled: modelData.enabled

View File

@ -28,6 +28,7 @@ Item {
id: textItem
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
visible: !buttons.visible
text: {
if (root.accepted) {

View File

@ -0,0 +1,50 @@
import QtQuick 2.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import shared 1.0
import utils 1.0
StatusFlatButton {
id: root
property int verificationStatus: -1
signal activate()
enabled: verificationStatus == Constants.verificationStatus.verifying ||
verificationStatus == Constants.verificationStatus.verified
size: StatusBaseButton.Size.Small
text: {
switch (verificationStatus) {
case Constants.verificationStatus.verifying:
return qsTr("Answer")
case Constants.verificationStatus.verified:
return qsTr("Edit Answer")
case Constants.verificationStatus.canceled:
return qsTr("Canceled")
case Constants.verificationStatus.declined:
return qsTr("Declined")
// That should never happen
case Constants.verificationStatus.trusted:
case Constants.verificationStatus.untrustworthy:
case Constants.verificationStatus.unverified:
default:
return qsTr("Unknown")
}
}
disabledTextColor: {
switch (verificationStatus) {
case Constants.verificationStatus.declined:
return Theme.palette.dangerColor1
case Constants.verificationStatus.trusted:
return Theme.palette.successColor1
default:
return Theme.palette.baseColor1
}
}
onClicked: root.activate()
}

View File

@ -25,6 +25,7 @@ Item {
id: textItem
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
visible: !pending
text: {
if (root.accepted) {

View File

@ -0,0 +1,53 @@
import QtQuick 2.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import shared 1.0
import utils 1.0
StatusFlatButton {
id: root
property int verificationStatus: -1
signal activate()
enabled: verificationStatus == Constants.verificationStatus.verifying ||
verificationStatus == Constants.verificationStatus.verified
size: StatusBaseButton.Size.Small
text: {
switch (verificationStatus) {
case Constants.verificationStatus.verifying:
return qsTr("Sent")
case Constants.verificationStatus.verified:
return qsTr("Verify Identity")
case Constants.verificationStatus.canceled:
return qsTr("Canceled")
case Constants.verificationStatus.declined:
return qsTr("Verification Request Declined")
case Constants.verificationStatus.trusted:
return qsTr("Identity Verified")
case Constants.verificationStatus.untrustworthy:
return qsTr("Marked Untrustworthy")
case Constants.verificationStatus.unverified:
default:
return qsTr("Unknown")
}
}
disabledTextColor: {
switch (verificationStatus) {
case Constants.verificationStatus.declined:
case Constants.verificationStatus.unverified:
case Constants.verificationStatus.untrustworthy:
return Theme.palette.dangerColor1
case Constants.verificationStatus.trusted:
return Theme.palette.successColor1
default:
return Theme.palette.baseColor1
}
}
onClicked: root.activate()
}

View File

@ -38,6 +38,7 @@ Popup {
property int mentionsCount: 0
property int repliesCount: 0
property int contactRequestsCount: 0
property int identityRequestsCount: 0
property int membershipCount: 0
property ActivityCenterStore activityCenterStore
@ -57,6 +58,8 @@ Popup {
return notificationType === Constants.activityCenterNotificationTypeReply
case ActivityCenterPopup.ActivityCategory.ContactRequests:
return notificationType === Constants.activityCenterNotificationTypeContactRequest
case ActivityCenterPopup.ActivityCategory.IdentityVerification:
return notificationType === Constants.activityCenterNotificationTypeContactVerification
case ActivityCenterPopup.ActivityCategory.Membership:
return notificationType === Constants.activityCenterNotificationTypeCommunityInvitation ||
notificationType === Constants.activityCenterNotificationTypeCommunityMembershipRequest ||
@ -78,6 +81,9 @@ Popup {
case Constants.activityCenterNotificationTypeContactRequest:
root.contactRequestsCount += cnt;
break;
case Constants.activityCenterNotificationTypeContactVerification:
root.identityRequestsCount += cnt;
break;
case Constants.activityCenterNotificationTypeCommunityInvitation:
root.membershipCount += cnt;
break;
@ -145,6 +151,7 @@ Popup {
hasReplies: root.repliesCount > 0
hasMentions: root.mentionsCount > 0
hasContactRequests: root.contactRequestsCount > 0
hasIdentityRequests: root.identityRequestsCount > 0
hasMembership: root.membershipCount > 0
hideReadNotifications: activityCenterStore.hideReadNotifications
currentActivityCategory: root.currentActivityCategory
@ -190,6 +197,8 @@ Popup {
return replyNotificationComponent
case Constants.activityCenterNotificationTypeContactRequest:
return contactRequestNotificationComponent
case Constants.activityCenterNotificationTypeContactVerification:
return verificationRequestNotificationComponent
case Constants.activityCenterNotificationTypeCommunityInvitation:
return communityInvitationNotificationComponent
case Constants.activityCenterNotificationTypeCommunityMembershipRequest:
@ -238,6 +247,17 @@ Popup {
onCloseActivityCenter: root.close()
}
}
Component {
id: verificationRequestNotificationComponent
ActivityNotificationContactVerification {
filteredIndex: parent.filteredIndex
notification: parent.notification
store: root.store
activityCenterStore: root.activityCenterStore
onCloseActivityCenter: root.close()
}
}
Component {
id: communityInvitationNotificationComponent

View File

@ -0,0 +1,82 @@
import QtQuick 2.14
import QtQuick.Layouts 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as CoreUtils
import StatusQ.Components 0.1
import shared 1.0
import shared.panels 1.0
import utils 1.0
import "../panels"
ActivityNotificationMessage {
id: root
readonly property bool isOutgoingRequest: notification && notification.message.senderId === root.store.contactsStore.myPublicKey
readonly property string contactId: notification ? isOutgoingRequest ? notification.chatId : notification.author : ""
readonly property var contactDetails: notification ? Utils.getContactDetailsAsJson(contactId, false) : null
messageDetails.messageText: {
if (!notification)
return ""
return root.isOutgoingRequest ? notification.repliedMessage.messageText : notification.message.messageText
}
messageDetails.amISender: false
messageDetails.sender.id: contactId
messageDetails.sender.displayName: contactDetails ? contactDetails.displayName : ""
messageDetails.sender.secondaryName: contactDetails ? contactDetails.localNickname : ""
messageDetails.sender.trustIndicator: contactDetails ? contactDetails.trustStatus : 0
messageDetails.sender.profileImage.name: contactDetails ? contactDetails.displayIcon : ""
messageDetails.sender.profileImage.assetSettings.isImage: true
messageDetails.sender.profileImage.pubkey: contactId
messageDetails.sender.profileImage.colorId: Utils.colorIdForPubkey(notification ? contactId : "")
messageDetails.sender.profileImage.colorHash: Utils.getColorHashAsJson(notification ? contactId : "", contactDetails.ensVerified)
messageSubheaderComponent: StatusBaseText {
text: {
if (!notification)
return ""
if (root.isOutgoingRequest) {
return qsTr("To verify their identity you asked: %1").arg(CoreUtils.Utils.stripHtmlTags(notification.message.messageText))
}
return qsTr("Identity Verification Question:")
}
wrapMode: Text.Wrap
color: Theme.palette.baseColor1
font.weight: Font.Medium
font.italic: true
font.pixelSize: 15
}
ctaComponent: isOutgoingRequest ? outgoingContactVerificationCta : incomingContactVerificationCta
Component {
id: outgoingContactVerificationCta
OutgoingContactVerificationCta {
verificationStatus: notification ? notification.verificationStatus : Constants.verificationStatus.unverified
onActivate: {
Global.openOutgoingIDRequestPopup(root.contactId, popup => {})
root.closeActivityCenter()
}
}
}
Component {
id: incomingContactVerificationCta
IncomingContactVerificationCta {
verificationStatus: notification ? notification.verificationStatus : Constants.verificationStatus.unverified
onActivate: {
Global.openIncomingIDRequestPopup(root.contactId, popup => {})
root.closeActivityCenter()
}
}
}
}

View File

@ -383,6 +383,7 @@ QtObject {
readonly property int declined: 3
readonly property int canceled: 4
readonly property int trusted: 5
readonly property int untrustworthy: 6
}
readonly property QtObject contactsPanelUsage: QtObject {
@ -577,6 +578,7 @@ QtObject {
readonly property int activityCenterNotificationTypeCommunityRequest: 7
readonly property int activityCenterNotificationTypeCommunityMembershipRequest: 8
readonly property int activityCenterNotificationTypeCommunityKicked: 9
readonly property int activityCenterNotificationTypeContactVerification: 10
readonly property int activityCenterMembershipStatusPending: 1
readonly property int activityCenterMembershipStatusAccepted: 2

View File

@ -730,8 +730,7 @@ QtObject {
return (str.length > maxLength) ? str.substr(0, maxLength-4) + '...' : str;
}
function escapeHtml(unsafeStr)
{
function escapeHtml(unsafeStr) {
return unsafeStr
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")

2
vendor/status-go vendored

@ -1 +1 @@
Subproject commit 6abbe98cd20b639d0c7298091f00262e876f3e17
Subproject commit 9137257638bb5b9f6d7e336d0a2a24d65838a2ac