2022-06-22 12:16:21 +00:00
import QtQuick 2.13
import QtQuick . Controls 2.13
import QtQuick . Layouts 1.13
import QtGraphicalEffects 1.13
import utils 1.0
import shared 1.0
import shared . popups 1.0
import shared . stores 1.0
2022-06-28 18:11:18 +00:00
import shared . views . chat 1.0
2022-06-22 12:16:21 +00:00
import shared . controls . chat 1.0
2022-06-28 18:11:18 +00:00
import shared . panels 1.0
2022-06-22 12:16:21 +00:00
import StatusQ . Core 0.1
import StatusQ . Core . Theme 0.1
import StatusQ . Components 0.1
import StatusQ . Controls 0.1
import StatusQ . Popups 0.1
Rectangle {
id: root
property Popup parentPopup
property var profileStore
property var contactsStore
property string userPublicKey: profileStore . pubkey
property string userDisplayName: profileStore . displayName
property string userName: profileStore . username
property string userNickname: profileStore . details . localNickname
property string userEnsName: profileStore . ensName
property string userIcon: profileStore . profileLargeImage
property string text: ""
property bool userIsEnsVerified: profileStore . details . ensVerified
property bool userIsBlocked: profileStore . details . isBlocked
property bool isCurrentUser: profileStore . pubkey === userPublicKey
property bool isAddedContact: false
2022-06-28 18:11:18 +00:00
property int userTrustStatus: Constants . trustStatus . unknown
2022-07-07 19:44:54 +00:00
property int outgoingVerificationStatus: Constants . verificationStatus . unverified
2022-06-28 18:11:18 +00:00
property string challenge: ""
property string response: ""
property bool userIsUntrustworthy: false
property bool userTrustIsUnknown: false
2022-07-06 16:51:56 +00:00
property bool isContact: false
2022-06-28 18:11:18 +00:00
property bool isVerificationSent: false
property bool isVerified: false
property bool isTrusted: false
property bool hasReceivedVerificationRequest: false
property bool showRemoveVerified: false
property bool showVerifyIdentitySection: false
property bool showVerificationPendingSection: false
property bool showIdentityVerified: false
property bool showIdentityVerifiedUntrustworthy: false
property string verificationChallenge: ""
property string verificationResponse: ""
property string verificationResponseDisplayName: ""
property string verificationResponseIcon: ""
property string verificationRequestedAt: ""
property string verificationRepliedAt: ""
2022-06-22 12:16:21 +00:00
readonly property alias qrCodePopup: qrCodePopup
readonly property alias unblockContactConfirmationDialog: unblockContactConfirmationDialog
readonly property alias blockContactConfirmationDialog: blockContactConfirmationDialog
readonly property alias removeContactConfirmationDialog: removeContactConfirmationDialog
2022-06-28 18:11:18 +00:00
readonly property alias wizardAnimation: wizardAnimation
readonly property alias challengeTxt: challengeTxt
readonly property alias stepsListModel: stepsListModel
2022-07-06 19:27:04 +00:00
readonly property alias nicknamePopup: nicknamePopup
2022-06-28 18:11:18 +00:00
readonly property int animationDuration: 500
2022-06-22 12:16:21 +00:00
signal contactUnblocked ( publicKey: string )
signal contactBlocked ( publicKey: string )
signal contactAdded ( publicKey: string )
signal contactRemoved ( publicKey: string )
signal nicknameEdited ( publicKey: string )
implicitWidth: modalContent . implicitWidth
implicitHeight: modalContent . implicitHeight
color: Theme . palette . statusAppLayout . backgroundColor
radius: 8
QtObject {
id: d
readonly property string subTitle: root . userIsEnsVerified ? root.userName : Utils . getElidedCompressedPk ( userPublicKey )
readonly property int subTitleElide: Text . ElideMiddle
}
2022-06-28 18:11:18 +00:00
SequentialAnimation {
id: wizardAnimation
ScriptAction {
id: step1
property int loadingTime: 0
Behavior on loadingTime { NumberAnimation { duration: animationDuration } }
onLoadingTimeChanged: {
if ( isVerificationSent ) {
stepsListModel . setProperty ( 1 , "loadingTime" , step1 . loadingTime ) ;
}
}
script: {
step1 . loadingTime = animationDuration ;
stepsListModel . setProperty ( 0 , "loadingTime" , step1 . loadingTime ) ;
if ( isVerificationSent ) {
stepsListModel . setProperty ( 0 , "stepCompleted" , true ) ;
}
}
}
PauseAnimation {
duration: animationDuration + 100
}
ScriptAction {
id: step2
property int loadingTime: 0
Behavior on loadingTime { NumberAnimation { duration: animationDuration } }
onLoadingTimeChanged: {
if ( isVerificationSent && ! ! verificationResponse ) {
stepsListModel . setProperty ( 2 , "loadingTime" , step2 . loadingTime ) ;
}
}
script: {
if ( isVerificationSent && ! ! verificationChallenge ) {
step2 . loadingTime = animationDuration ;
if ( isVerificationSent && ! ! verificationResponse ) {
stepsListModel . setProperty ( 1 , "stepCompleted" , true ) ;
}
}
}
}
PauseAnimation {
duration: animationDuration + 100
}
ScriptAction {
script: {
2022-07-07 19:44:54 +00:00
if ( outgoingVerificationStatus === Constants . verificationStatus . trusted ) {
2022-06-28 18:11:18 +00:00
stepsListModel . setProperty ( 2 , "stepCompleted" , true ) ;
}
}
}
}
2022-06-22 12:16:21 +00:00
ColumnLayout {
id: modalContent
anchors.fill: parent
Item {
Layout.fillWidth: true
2022-06-28 18:11:18 +00:00
implicitHeight: 32
2022-06-22 12:16:21 +00:00
}
ProfileHeader {
Layout.fillWidth: true
displayName: root . userDisplayName
pubkey: root . userPublicKey
2022-07-06 19:07:33 +00:00
icon: root . isCurrentUser ? root.profileStore.profileLargeImage : root . userIcon
2022-06-28 18:11:18 +00:00
trustStatus: root . userTrustStatus
2022-07-06 16:51:56 +00:00
isContact: root . isContact
2022-06-28 18:11:18 +00:00
store: root . profileStore
2022-07-11 09:38:39 +00:00
isCurrentUser: root . isCurrentUser
2022-06-22 12:16:21 +00:00
displayNameVisible: false
2022-06-28 18:11:18 +00:00
displayNamePlusIconsVisible: true
pubkeyVisibleWithCopy: true
2022-06-22 12:16:21 +00:00
pubkeyVisible: false
imageSize: ProfileHeader . ImageSize . Middle
editImageButtonVisible: root . isCurrentUser
2022-06-28 18:11:18 +00:00
onEditClicked: {
if ( ! isCurrentUser ) {
nicknamePopup . open ( )
} else {
Global . openEditDisplayNamePopup ( )
}
}
2022-06-22 12:16:21 +00:00
}
StatusBanner {
Layout.fillWidth: true
visible: root . userIsBlocked
type: StatusBanner . Type . Danger
statusText: qsTr ( "Blocked" )
}
StatusDescriptionListItem {
Layout.fillWidth: true
2022-06-28 18:11:18 +00:00
visible: ! showVerifyIdentitySection && ! showVerificationPendingSection && ! showIdentityVerified
2022-06-22 12:16:21 +00:00
title: qsTr ( "Chat key" )
subTitle: Utils . getCompressedPk ( root . userPublicKey )
subTitleComponent.elide: Text . ElideMiddle
subTitleComponent.width: 320
subTitleComponent.font.family: Theme . palette . monoFont . name
tooltip.text: qsTr ( "Copied to clipboard" )
tooltip.timeout: 1000
icon.name: "copy"
iconButton.onClicked: {
globalUtils . copyToClipboard ( subTitle )
tooltip . open ( ) ;
}
}
StatusDescriptionListItem {
Layout.fillWidth: true
2022-06-28 18:11:18 +00:00
visible: ! showVerifyIdentitySection && ! showVerificationPendingSection && ! showIdentityVerified
2022-06-22 12:16:21 +00:00
title: qsTr ( "Share Profile URL" )
subTitle: {
let user = ""
if ( isCurrentUser ) {
2022-07-11 09:38:39 +00:00
user = root . profileStore . ensName !== "" ? root.profileStore.ensName : Utils . elideText ( root . profileStore . pubkey , 5 )
2022-06-22 12:16:21 +00:00
} else if ( userIsEnsVerified ) {
user = userEnsName
}
if ( user === "" ) {
2022-07-11 09:38:39 +00:00
user = Utils . elideText ( userPublicKey , 5 )
2022-06-22 12:16:21 +00:00
}
return Constants . userLinkPrefix + user ;
}
tooltip.text: qsTr ( "Copied to clipboard" )
tooltip.timeout: 1000
icon.name: "copy"
iconButton.onClicked: {
let user = ""
if ( isCurrentUser ) {
user = root . profileStore . ensName !== "" ? root.profileStore.ensName : root . profileStore . pubkey
} else {
user = ( userEnsName !== "" ? userEnsName : userPublicKey )
}
root . profileStore . copyToClipboard ( Constants . userLinkPrefix + user )
tooltip . open ( ) ;
}
}
2022-06-28 18:11:18 +00:00
ListModel {
id: stepsListModel
2022-07-11 09:25:36 +00:00
ListElement { description: qsTr ( "Send Request" ) ; loadingTime: 0 ; stepCompleted: false }
ListElement { description: qsTr ( "Receive Response" ) ; loadingTime: 0 ; stepCompleted: false }
ListElement { description: qsTr ( "Confirm Identity" ) ; loadingTime: 0 ; stepCompleted: false }
2022-06-28 18:11:18 +00:00
}
StatusWizardStepper {
id: wizardStepper
maxDuration: animationDuration
visible: showVerifyIdentitySection || showVerificationPendingSection || showIdentityVerified || showIdentityVerifiedUntrustworthy
width: parent . width
stepsModel: stepsListModel
}
Separator {
visible: wizardStepper . visible
implicitHeight: 32
}
StatusBaseText {
id: confirmLbl
visible: showIdentityVerified
text: qsTr ( "You have confirmed %1's identity. From now on this verification emblem will always be displayed alongside %1's nickname." ) . arg ( userIsEnsVerified ? userEnsName : userDisplayName )
font.pixelSize: Style . current . additionalTextSize
horizontalAlignment : Text . AlignHCenter
Layout.alignment: Qt . AlignHCenter
Layout.preferredWidth: 363
wrapMode: Text . WordWrap
color: Theme . palette . baseColor1
}
StatusBaseText {
id: confirmUntrustworthyLbl
visible: showIdentityVerifiedUntrustworthy
text: qsTr ( "You have marked %1 as Untrustworthy. From now on this Untrustworthy emblem will always be displayed alongside %1's nickname." ) . arg ( userIsEnsVerified ? userEnsName : userDisplayName )
font.pixelSize: Style . current . additionalTextSize
horizontalAlignment : Text . AlignHCenter
Layout.alignment: Qt . AlignHCenter
Layout.preferredWidth: 363
wrapMode: Text . WordWrap
color: Theme . palette . baseColor1
}
Item {
visible: checkboxIcon . visible || dangerIcon . visible
Layout.fillWidth: true
implicitHeight: visible ? 16 : 0
}
StatusRoundIcon {
id: checkboxIcon
visible: confirmLbl . visible
icon.name: "checkbox"
icon.width: 16
icon.height: 16
icon.color: Theme . palette . white
Layout.alignment: Qt . AlignHCenter
color: Theme . palette . primaryColor1
width: 32
height: 32
}
2022-06-22 12:16:21 +00:00
StatusDescriptionListItem {
Layout.fillWidth: true
2022-06-28 18:11:18 +00:00
visible: ! showVerifyIdentitySection && ! showVerificationPendingSection && ! showIdentityVerified
title: root . userIsEnsVerified ? qsTr ( "ENS username" ) : qsTr ( "Username" )
subTitle: root . userIsEnsVerified ? root.userEnsName : root . userName
tooltip.text: qsTr ( "Copied to clipboard" )
tooltip.timeout: 1000
icon.name: "copy"
iconButton.onClicked: {
globalUtils . copyToClipboard ( subTitle )
tooltip . open ( ) ;
2022-06-22 12:16:21 +00:00
}
}
2022-06-28 18:11:18 +00:00
StatusRoundIcon {
id: dangerIcon
visible: confirmUntrustworthyLbl . visible
icon.name: "tiny/subtract"
icon.width: 5
icon.height: 21
icon.color: Theme . palette . white
Layout.alignment: Qt . AlignHCenter
color: Theme . palette . dangerColor1
width: 32
height: 32
}
Item {
visible: checkboxIcon . visible || dangerIcon . visible
height: visible ? 16 : 0
Layout.fillWidth: true
}
StatusInput {
id: challengeTxt
visible: showVerifyIdentitySection
charLimit: 280
input.text: root . challenge
Layout.fillWidth: true
Layout.rightMargin: d . contentMargins
Layout.leftMargin: d . contentMargins
input.multiline: true
2022-07-26 09:49:28 +00:00
minimumHeight: 152
maximumHeight: 152
2022-07-22 10:28:04 +00:00
placeholderText: qsTr ( "Ask a question that only the real %1 will be able to answer e.g. a question about a shared experience, or ask Mark to enter a code or phrase you have sent to them via a different communication channel (phone, post, etc...)." ) . arg ( userIsEnsVerified ? userEnsName : userDisplayName )
2022-06-28 18:11:18 +00:00
}
MessageView {
id: challengeMessage
visible: root . showVerificationPendingSection
Layout.fillWidth: true
isMessage: true
shouldRepeatHeader: true
messageTimestamp: root . verificationRequestedAt
senderDisplayName: userProfile . name
senderIcon: userProfile . icon
message: root . verificationChallenge
messageContentType: Constants . messageContentType . messageType
placeholderMessage: true
}
MessageView {
id: responseMessage
visible: root . showVerificationPendingSection && ! ! root . verificationResponse
width: parent . width
isMessage: true
shouldRepeatHeader: true
messageTimestamp: root . verificationRepliedAt
senderDisplayName: root . verificationResponseDisplayName
senderIcon: root . verificationResponseIcon
message: root . verificationResponse
messageContentType: Constants . messageContentType . messageType
placeholderMessage: true
}
Item {
visible: waitingForText . visible
height: 32
Layout.fillWidth: true
}
StatusBaseText {
id: waitingForText
visible: showVerificationPendingSection && ! verificationResponse
text: qsTr ( "Waiting for %1's response..." ) . arg ( userIsEnsVerified ? userEnsName : userDisplayName )
font.pixelSize: Style . current . additionalTextSize
horizontalAlignment : Text . AlignHCenter
Layout.alignment: Qt . AlignHCenter
Layout.preferredWidth: 363
wrapMode: Text . WordWrap
color: Theme . palette . baseColor1
}
2022-06-22 12:16:21 +00:00
Item {
2022-06-28 18:11:18 +00:00
height: 32
2022-06-22 12:16:21 +00:00
Layout.fillWidth: true
}
}
// TODO: replace with StatusModal
ModalPopup {
id: qrCodePopup
width: 320
height: 320
Image {
asynchronous: true
fillMode: Image . PreserveAspectFit
source: globalUtils . qrCode ( userPublicKey )
anchors.horizontalCenter: parent . horizontalCenter
height: 212
width: 212
mipmap: true
smooth: false
}
}
UnblockContactConfirmationDialog {
id: unblockContactConfirmationDialog
onUnblockButtonClicked: {
root . contactsStore . unblockContact ( userPublicKey )
unblockContactConfirmationDialog . close ( ) ;
root . contactUnblocked ( userPublicKey )
}
}
BlockContactConfirmationDialog {
id: blockContactConfirmationDialog
onBlockButtonClicked: {
root . contactsStore . blockContact ( userPublicKey )
blockContactConfirmationDialog . close ( ) ;
root . contactBlocked ( userPublicKey )
}
}
ConfirmationDialog {
id: removeContactConfirmationDialog
header.title: qsTr ( "Remove contact" )
confirmationText: qsTr ( "Are you sure you want to remove this contact?" )
onConfirmButtonClicked: {
if ( isAddedContact ) {
root . contactsStore . removeContact ( userPublicKey ) ;
}
removeContactConfirmationDialog . close ( ) ;
root . contactRemoved ( userPublicKey )
}
}
NicknamePopup {
id: nicknamePopup
nickname: root . userNickname
header.subTitle: d . subTitle
header.subTitleElide: d . subTitleElide
onEditDone: {
if ( root . userNickname !== newNickname )
{
root . userNickname = newNickname ;
root . contactsStore . changeContactNickname ( userPublicKey , newNickname ) ;
}
root . nicknameEdited ( userPublicKey )
}
}
}