status-desktop/ui/imports/shared/views/ProfileView.qml

485 lines
17 KiB
QML
Raw Normal View History

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
import shared.views.chat 1.0
2022-06-22 12:16:21 +00:00
import shared.controls.chat 1.0
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.Core.Utils 0.1 as StatusQUtils
2022-06-22 12:16:21 +00:00
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
import "../panels"
2022-06-22 12:16:21 +00:00
Rectangle {
id: root
property Popup parentPopup
property var profileStore
property var contactsStore
2022-06-22 12:16:21 +00:00
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 userBio: profileStore.bio
property string userSocialLinks: profileStore.socialLinksJson
2022-06-22 12:16:21 +00:00
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
property int userTrustStatus: Constants.trustStatus.unknown
property int outgoingVerificationStatus: Constants.verificationStatus.unverified
property string challenge: ""
property string response: ""
property bool userIsUntrustworthy: false
property bool userTrustIsUnknown: false
property bool isContact: false
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
readonly property alias wizardAnimation: wizardAnimation
readonly property alias challengeTxt: challengeTxt
readonly property alias stepsListModel: stepsListModel
readonly property alias nicknamePopup: nicknamePopup
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)
objectName: "profileView"
2022-06-22 12:16:21 +00:00
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
}
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: {
if (outgoingVerificationStatus === Constants.verificationStatus.trusted) {
stepsListModel.setProperty(2, "stepCompleted", true);
}
}
}
}
2022-06-22 12:16:21 +00:00
ColumnLayout {
id: modalContent
anchors.fill: parent
Item {
Layout.fillWidth: true
implicitHeight: 32
2022-06-22 12:16:21 +00:00
}
ProfileHeader {
Layout.fillWidth: true
displayName: root.userDisplayName
pubkey: root.userPublicKey
icon: root.isCurrentUser ? root.profileStore.profileLargeImage : root.userIcon
trustStatus: root.userTrustStatus
isContact: root.isContact
store: root.profileStore
isCurrentUser: root.isCurrentUser
userIsEnsVerified: root.userIsEnsVerified
2022-06-22 12:16:21 +00:00
displayNameVisible: false
displayNamePlusIconsVisible: true
pubkeyVisibleWithCopy: true
2022-06-22 12:16:21 +00:00
pubkeyVisible: false
imageSize: ProfileHeader.ImageSize.Middle
editImageButtonVisible: root.isCurrentUser
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")
}
ProfileBioSocialsPanel {
Layout.fillWidth: true
Layout.leftMargin: 16
Layout.rightMargin: 16
bio: root.userBio
userSocialLinksJson: root.userSocialLinks
}
2022-06-22 12:16:21 +00:00
StatusDescriptionListItem {
Layout.fillWidth: true
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
asset.name: "copy"
2022-06-22 12:16:21 +00:00
iconButton.onClicked: {
globalUtils.copyToClipboard(subTitle)
tooltip.open();
}
}
StatusDescriptionListItem {
Layout.fillWidth: true
visible: !showVerifyIdentitySection && !showVerificationPendingSection && !showIdentityVerified
2022-06-22 12:16:21 +00:00
title: qsTr("Share Profile URL")
subTitle: {
let user = ""
if (isCurrentUser) {
user = root.profileStore.ensName !== "" ? root.profileStore.ensName : StatusQUtils.Utils.elideText(root.profileStore.pubkey, 5)
2022-06-22 12:16:21 +00:00
} else if (userIsEnsVerified) {
user = userEnsName
}
if (user === ""){
user = StatusQUtils.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
asset.name: "copy"
2022-06-22 12:16:21 +00:00
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();
}
}
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}
}
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(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(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
asset.name: "checkbox"
asset.width: 16
asset.height: 16
asset.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
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
asset.name: "copy"
iconButton.onClicked: {
globalUtils.copyToClipboard(subTitle)
tooltip.open();
2022-06-22 12:16:21 +00:00
}
}
StatusRoundIcon {
id: dangerIcon
visible: confirmUntrustworthyLbl.visible
asset.name: "tiny/subtract"
asset.width: 5
asset.height: 21
asset.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
minimumHeight: 152
maximumHeight: 152
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 %1 to enter a code or phrase you have sent to them via a different communication channel (phone, post, etc...).").arg(userDisplayName)
}
MessageView {
id: challengeMessage
visible: root.showVerificationPendingSection
Layout.fillWidth: true
isMessage: true
shouldRepeatHeader: true
messageTimestamp: root.verificationRequestedAt
senderId: profileStore.pubkey
senderDisplayName: userProfile.name
senderIcon: userProfile.icon
messageText: root.verificationChallenge
messageContentType: Constants.messageContentType.messageType
placeholderMessage: true
}
MessageView {
id: responseMessage
visible: root.showVerificationPendingSection && !!root.verificationResponse
Layout.fillWidth: true
isMessage: true
shouldRepeatHeader: true
messageTimestamp: root.verificationRepliedAt
senderId: root.userPublicKey
senderDisplayName: root.verificationResponseDisplayName
senderIcon: root.verificationResponseIcon
messageText: 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 {
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)
}
}
}