feat: New Profile Modal

Fixes: #7360
This commit is contained in:
Lukáš Tinkl 2022-09-27 23:26:26 +02:00 committed by Lukáš Tinkl
parent 1652681897
commit 7a407662e2
74 changed files with 1222 additions and 1306 deletions

View File

@ -760,6 +760,7 @@ method getContactDetailsAsJson*[T](self: Module[T], publicKey: string, getVerifi
requestStatus = self.getVerificationRequestFrom(publicKey).status.int
let jsonObj = %* {
"displayName": name,
"optionalName": self.controller.getContactDetails(contact.id).optionalName, # original display name, if renamed
"displayIcon": contact.image.thumbnail,
"publicKey": contact.id,
"name": contact.name,

View File

@ -229,6 +229,7 @@ QtObject:
self.items[ind].ensName = ensName
self.items[ind].localNickname = localNickname
self.items[ind].alias = alias
self.items[ind].icon = icon
self.items[ind].isUntrustworthy = isUntrustworthy
let index = self.createIndex(ind, 0, nil)

View File

@ -1,21 +1,23 @@
import QtQuick 2.0
import QtQuick 2.14
import StatusQ.Core 0.1
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
Row {
id: root
property bool isContact: false
property var trustIndicator: StatusContactVerificationIcons.TrustedType.None
property int trustIndicator: StatusContactVerificationIcons.TrustedType.None
property bool tiny: true
property StatusAssetSettings mutualConnectionIcon: StatusAssetSettings {
name: "tiny/tiny-contact"
name: root.tiny ? "tiny/tiny-contact" : "tiny/contact"
color: Theme.palette.indirectColor1
width: dummyImage.width
height: dummyImage.height
bgWidth: 10
bgHeight: 10
width: Math.min(bgWidth, dummyImage.width)
height: Math.min(bgHeight, dummyImage.height)
bgWidth: root.tiny ? 10 : 16.5
bgHeight: root.tiny ? 10 : 16.5
bgColor: Theme.palette.primaryColor1
// Only used to get implicit width and height from the actual image
property Image dummyImage: Image {
@ -26,12 +28,13 @@ Row {
property StatusAssetSettings trustContactIcon: StatusAssetSettings {
// None and Untrustworthy types, same aspect (Icon will not be visible in case of None type):
name: root.trustIndicator === StatusContactVerificationIcons.TrustedType.Verified ? "tiny/tiny-checkmark" : "tiny/subtract"
name: root.trustIndicator === StatusContactVerificationIcons.TrustedType.Verified ? root.tiny ? "tiny/tiny-checkmark" : "tiny/checkmark"
: root.tiny ? "tiny/tiny-exclamation" : "tiny/exclamation"
color: Theme.palette.indirectColor1
width: dummyImage.width
height: dummyImage.height
bgWidth: 10
bgHeight: 10
width: Math.min(bgWidth, dummyImage.width)
height: Math.min(bgHeight, dummyImage.height)
bgWidth: root.tiny ? 10 : 16
bgHeight: root.tiny ? 10 : 16
bgColor: root.trustIndicator === StatusContactVerificationIcons.TrustedType.Verified ? Theme.palette.primaryColor1 : Theme.palette.dangerColor1
// Only used to get implicit width and height from the actual image
property Image dummyImage: Image {
@ -49,27 +52,34 @@ Row {
spacing: 4
visible: root.isContact || (root.trustIndicator !== StatusContactVerificationIcons.TrustedType.None)
HoverHandler {
id: hoverHandler
}
StatusToolTip {
text: {
if (root.isContact) {
if (root.trustIndicator === StatusContactVerificationIcons.TrustedType.Verified)
return qsTr("Verified contact")
if (root.trustIndicator === StatusContactVerificationIcons.TrustedType.Untrustworthy)
return qsTr("Untrustworthy contact")
return qsTr("Contact")
}
if (root.trustIndicator === StatusContactVerificationIcons.TrustedType.Untrustworthy)
return qsTr("Untrustworthy")
return ""
}
visible: hoverHandler.hovered && text
}
StatusRoundIcon {
visible: root.isContact
asset.name: root.mutualConnectionIcon.name
asset.width: root.mutualConnectionIcon.width
asset.height: root.mutualConnectionIcon.height
asset.rotation: root.mutualConnectionIcon.rotation
asset.color: root.mutualConnectionIcon.color
asset.bgColor: root.mutualConnectionIcon.bgColor
asset.bgWidth: root.mutualConnectionIcon.bgWidth
asset.bgHeight: root.mutualConnectionIcon.bgHeight
visible: root.isContact && root.trustIndicator !== StatusContactVerificationIcons.TrustedType.Verified
asset: root.mutualConnectionIcon
}
StatusRoundIcon {
visible: root.trustIndicator !== StatusContactVerificationIcons.TrustedType.None
asset.name: root.trustContactIcon.name
asset.width: root.trustContactIcon.width
asset.height: root.trustContactIcon.height
asset.rotation: root.trustContactIcon.rotation
asset.color: root.trustContactIcon.color
asset.bgColor: root.trustContactIcon.bgColor
asset.bgWidth: root.trustContactIcon.bgWidth
asset.bgHeight: root.trustContactIcon.bgHeight
asset: root.trustContactIcon
}
}

View File

@ -70,6 +70,7 @@ Button {
icon.height: 24
icon.width: 24
icon.color: d.textColor
background: Rectangle {
radius: root.radius
@ -90,7 +91,7 @@ Button {
rotation: root.asset.rotation
opacity: !loading && root.icon.name !== ""
visible: root.icon.name !== ""
color: d.textColor
color: root.icon.color
}
StatusEmoji {
Layout.preferredWidth: visible ? root.icon.width : 0

View File

@ -14,7 +14,7 @@ Button {
property var type: StatusPickerButton.Type.Next
/*!
\qmlproperty StatusAssetSettings StatusPickerButton::image
\qmlproperty StatusAssetSettings StatusPickerButton::asset
This property holds the image settings information.
*/
property StatusAssetSettings asset: StatusAssetSettings {

View File

@ -57,10 +57,14 @@ Item {
onCenteredChanged: flow.onPositioningComplete()
implicitHeight: flow.implicitHeight
Flow {
id: flow
anchors.fill: parent
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
onPositioningComplete: {
if (!root.centered || children.length === 0)

View File

@ -14,7 +14,7 @@ QtObject {
FontSizeXXL
}
property QtObject palette: StatusLightTheme {}
property ThemePalette palette: StatusLightTheme {}
property int primaryTextFontSize: 15
property int secondaryTextFontSize: 14

View File

@ -10,6 +10,8 @@ import StatusQ.Core.Theme 0.1
Dialog {
id: root
property string subtitle
anchors.centerIn: Overlay.overlay
padding: 16
@ -25,8 +27,9 @@ Dialog {
background: StatusDialogBackground {}
header: StatusDialogHeader {
visible: root.title
visible: root.title || root.subtitle
headline.title: root.title
headline.subtitle: root.subtitle
actions.closeButton.onClicked: root.close()
}

View File

@ -77,7 +77,7 @@ StatusModal {
validators: [
StatusRegularExpressionValidator {
regularExpression: /^#(?:[0-9a-fA-F]{3}){1,2}$/
errorMessage: qsTr("This is not a valid color")
errorMessage: qsTr("This is not a valid colour")
}
]
validationMode: StatusInput.ValidationMode.Always
@ -115,7 +115,7 @@ StatusModal {
}
StatusBaseText {
text: qsTr("Standart colours")
text: qsTr("Standard colours")
font.pixelSize: 15
}

View File

@ -1,4 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.624 8.41603C16.8538 8.07138 16.7607 7.60573 16.416 7.37596C16.0714 7.1462 15.6057 7.23933 15.376 7.58397L9.88343 15.8228L7.53033 13.4697C7.23744 13.1768 6.76256 13.1768 6.46967 13.4697C6.17678 13.7626 6.17678 14.2374 6.46967 14.5303L9.46967 17.5303C9.62855 17.6892 9.8503 17.7685 10.0739 17.7464C10.2975 17.7242 10.4994 17.603 10.624 17.416L16.624 8.41603Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20.5C7.30558 20.5 3.5 16.6944 3.5 12C3.5 7.30558 7.30558 3.5 12 3.5C16.6944 3.5 20.5 7.30558 20.5 12C20.5 16.6944 16.6944 20.5 12 20.5Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.5 12C20.5 16.6944 16.6944 20.5 12 20.5C7.30558 20.5 3.5 16.6944 3.5 12C3.5 7.30558 7.30558 3.5 12 3.5C16.6944 3.5 20.5 7.30558 20.5 12ZM22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12ZM16.7728 10.1161C17.0657 9.82318 17.0657 9.34831 16.7728 9.05541C16.4799 8.76252 16.0051 8.76252 15.7122 9.05541L10.5856 14.1819L8.28754 11.8838C7.99465 11.5909 7.51977 11.5909 7.22688 11.8838C6.93398 12.1767 6.93399 12.6516 7.22688 12.9445L10.0553 15.7729C10.3482 16.0658 10.8231 16.0658 11.116 15.7729L16.7728 10.1161Z" fill="#0D1625"/>
</svg>

Before

Width:  |  Height:  |  Size: 807 B

After

Width:  |  Height:  |  Size: 743 B

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 2C7.30964 2 6.75 2.55964 6.75 3.25V7.75C6.75 8.44036 7.30964 9 8 9C8.69036 9 9.25 8.44036 9.25 7.75V3.25C9.25 2.55964 8.69036 2 8 2ZM8 13.5C8.69036 13.5 9.25 12.9404 9.25 12.25C9.25 11.5596 8.69036 11 8 11C7.30964 11 6.75 11.5596 6.75 12.25C6.75 12.9404 7.30964 13.5 8 13.5Z" fill="#0D1625"/>
</svg>

After

Width:  |  Height:  |  Size: 448 B

View File

Before

Width:  |  Height:  |  Size: 531 B

After

Width:  |  Height:  |  Size: 531 B

View File

@ -1,5 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12 6.25C12.4142 6.25 12.75 6.58579 12.75 7V14C12.75 14.4142 12.4142 14.75 12 14.75C11.5858 14.75 11.25 14.4142 11.25 14V7C11.25 6.58579 11.5858 6.25 12 6.25Z" fill="black"/>
<path d="M12 18.75C12.6904 18.75 13.25 18.1904 13.25 17.5C13.25 16.8096 12.6904 16.25 12 16.25C11.3096 16.25 10.75 16.8096 10.75 17.5C10.75 18.1904 11.3096 18.75 12 18.75Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2C6.47715 2 2 6.47715 2 12ZM3.5 12C3.5 16.6944 7.30558 20.5 12 20.5C16.6944 20.5 20.5 16.6944 20.5 12C20.5 7.30558 16.6944 3.5 12 3.5C7.30558 3.5 3.5 7.30558 3.5 12Z" fill="black"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M12 3.75C7.44365 3.75 3.75 7.44365 3.75 12C3.75 16.5563 7.44365 20.25 12 20.25C16.5563 20.25 20.25 16.5563 20.25 12C20.25 7.44365 16.5563 3.75 12 3.75ZM2.25 12C2.25 6.61522 6.61522 2.25 12 2.25C17.3848 2.25 21.75 6.61522 21.75 12C21.75 17.3848 17.3848 21.75 12 21.75C6.61522 21.75 2.25 17.3848 2.25 12ZM12 6.25C12.4142 6.25 12.75 6.58579 12.75 7V13C12.75 13.4142 12.4142 13.75 12 13.75C11.5858 13.75 11.25 13.4142 11.25 13V7C11.25 6.58579 11.5858 6.25 12 6.25ZM13 17C13 17.5523 12.5523 18 12 18C11.4477 18 11 17.5523 11 17C11 16.4477 11.4477 16 12 16C12.5523 16 13 16.4477 13 17Z" fill="#0D1625"/>
</svg>

Before

Width:  |  Height:  |  Size: 791 B

After

Width:  |  Height:  |  Size: 750 B

View File

@ -37,9 +37,7 @@ ModalPopup {
Global.openProfilePopup(model.pubKey)
}
onBlockContactActionTriggered: {
blockContactConfirmationDialog.contactName = model.displayName
blockContactConfirmationDialog.contactAddress = model.pubKey
blockContactConfirmationDialog.open()
Global.blockContactRequested(model.pubKey, model.displayName)
}
onAcceptClicked: {
popup.store.acceptContactRequest(model.pubKey)
@ -54,14 +52,6 @@ ModalPopup {
width: parent.width
height: children[0].height
BlockContactConfirmationDialog {
id: blockContactConfirmationDialog
onBlockButtonClicked: {
popup.store.blockContact(blockContactConfirmationDialog.contactAddress)
blockContactConfirmationDialog.close()
}
}
ConfirmationDialog {
id: declineAllDialog
header.title: qsTr("Decline all contacts")

View File

@ -23,7 +23,6 @@ StatusDialog {
signal updateGroupChatDetails(string groupName, string groupColor, string groupImage)
anchors.centerIn: parent
title: qsTr("Edit group name and image")
width: 480
height: 610
@ -34,7 +33,7 @@ StatusDialog {
}
onOpened: {
groupName.forceActiveFocus(Qt.MouseFocusReason)
groupName.input.edit.forceActiveFocus()
groupName.text = root.activeGroupName.substring(0, d.nameCharLimit)
colorSelectionGrid.selectedColor = activeGroupColor

View File

@ -81,7 +81,7 @@ StatusModal {
}
StatusBaseText {
text: qsTr("%1 members").arg(nbMembers)
text: qsTr("%n member(s)", "", nbMembers)
font.pixelSize: 15
font.weight: Font.Medium
color: Theme.palette.directColor1

View File

@ -34,7 +34,7 @@ StatusModal {
root.channels = []
root.store.prepareEditCategoryModel(categoryId);
}
root.contentItem.categoryName.input.forceActiveFocus(Qt.MouseFocusReason)
root.contentItem.categoryName.input.edit.forceActiveFocus()
}
onClosed: destroy()
@ -42,9 +42,7 @@ StatusModal {
return contentItem.categoryName.valid
}
header.title: isEdit ?
qsTr("Edit category") :
qsTr("New category")
header.title: isEdit ? qsTr("Edit category") : qsTr("New category")
contentItem: Column {
property alias categoryName: nameInput
@ -59,6 +57,7 @@ StatusModal {
anchors.leftMargin: 16
input.edit.objectName: "createOrEditCommunityCategoryNameInput"
input.clearable: true
label: qsTr("Category title")
charLimit: maxCategoryNameLength
placeholderText: qsTr("Name the category")
@ -123,6 +122,8 @@ StatusModal {
anchors.horizontalCenter: parent.horizontalCenter
height: visible ? implicitHeight : 0
title: "#" + model.name
asset.width: 30
asset.height: 30
asset.emoji: model.emoji
asset.color: model.color
asset.imgIsIdenticon: false

View File

@ -1,8 +1,6 @@
import QtQuick 2.13
import QtQuick 2.14
import utils 1.0
import StatusQ.Core.Utils 0.1 as StatusQUtils
QtObject {
id: root

View File

@ -401,9 +401,9 @@ QtObject {
if (index > -1) {
const pk = link.substring(index + 3)
result.title = qsTr("Start a 1-on-1 chat with %1")
.arg(isChatKey(pk) ? globalUtils.generateAlias(pk) : ("@" + removeStatusEns(pk)))
.arg(Utils.isChatKey(pk) ? globalUtils.generateAlias(pk) : ("@" + Utils.removeStatusEns(pk)))
result.callback = function () {
if (isChatKey(pk)) {
if (Utils.isChatKey(pk)) {
chatCommunitySectionModule.createOneToOneChat("", pk, "")
} else {
// Not Refactored Yet

View File

@ -1,8 +1,4 @@
import QtQuick 2.3
import QtQuick.Controls 2.3
import QtQuick.Controls 2.12 as QQC2
import QtQuick.Layouts 1.3
import Qt.labs.platform 1.1
import QtQml.Models 2.14
ListModel {
ListElement {
@ -58,9 +54,3 @@ ListModel {
url: "QmY4QULmzFQ2AAbEuMvnd3Nd7qD8eWtyxiLD9CAf3kFZWU"
}
}
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -1,5 +1,4 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQml.Models 2.14
ListModel {
ListElement {
@ -24,9 +23,3 @@ ListModel {
thumbnail: "QmZdTTRiMvupRUWq6ctVbuPfEmc8Js53TmBKyjSYNHmGdi"
}
}
/*##^##
Designer {
D{i:0;autoSize:true;height:480;width:640}
}
##^##*/

View File

@ -108,7 +108,7 @@ ColumnLayout {
}
onOpenProfileClicked: {
Global.openProfilePopup(publicKey, null, state)
Global.openProfilePopup(publicKey, null)
}
onDeleteMessage: {

View File

@ -185,9 +185,6 @@ StatusPopupMenu {
Global.openPopup(deleteChatConfirmationDialogComponent)
close()
}
onClosed: {
destroy()
}
}
}

View File

@ -173,26 +173,12 @@ StatusSectionLayout {
}
}
ConfirmationDialog {
id: removeContactConfirmationDialog
header.title: qsTr("Remove contact")
confirmationText: qsTr("Are you sure you want to remove this contact?")
onConfirmButtonClicked: {
let pk = chatColumn.contactToRemove
if (Utils.getContactDetailsAsJson(pk).isAdded) {
root.contactsStore.removeContact(pk)
}
removeContactConfirmationDialog.parentPopup.close();
removeContactConfirmationDialog.close();
}
}
MessageContextMenuView {
id: quickActionMessageOptionsMenu
store: root.rootStore
onOpenProfileClicked: {
Global.openProfilePopup(publicKey, null, state)
Global.openProfilePopup(publicKey, null)
}
onCreateOneToOneChat: {
Global.changeAppSectionBySectionType(Constants.appSection.chat)

View File

@ -14,7 +14,6 @@ StatusDialog {
id: root
width: 480
anchors.centerIn: parent
closePolicy: Popup.NoAutoClose
header: StatusDialogHeader {

View File

@ -38,10 +38,6 @@ StatusSectionLayout {
}
}
Component.onCompleted: {
Global.privacyModuleInst = store.privacyStore.privacyModule
}
QtObject {
id: d
@ -106,6 +102,7 @@ StatusSectionLayout {
walletStore: root.store.walletStore
profileStore: root.store.profileStore
privacyStore: root.store.privacyStore
contactsStore: root.store.contactsStore
sectionTitle: root.store.getNameForSubsection(Constants.settingsSubsection.profile)
contentWidth: d.contentWidth
}

View File

@ -20,6 +20,8 @@ StatusListItem {
asset.letterSize: 14
asset.isLetterIdenticon: !!account.emoji
asset.bgColor: Theme.palette.primaryColor3
asset.width: 40
asset.height: 40
width: parent.width
components: !showShevronIcon ? [] : [ shevronIcon ]

View File

@ -70,7 +70,7 @@ StatusListItem {
asset.isImage: asset.name.includes("data")
asset.isLetterIdenticon: root.iconSource.toString() === ""
ringSettings {
ringSpecModel: Utils.getColorHashAsJson(root.publicKey)
ringSpecModel: d.ensVerified ? undefined : Utils.getColorHashAsJson(root.publicKey, true)
ringPxSize: Math.max(asset.width / 24.0)
}

View File

@ -16,7 +16,7 @@ QtObject {
property var profileSectionModuleInst: profileSectionModule
property bool fetchingUpdate: aboutModule.fetching
readonly property bool fetchingUpdate: aboutModuleInst.fetching
property ContactsStore contactsStore: ContactsStore {
contactsModule: profileSectionModuleInst.contactsModule

View File

@ -27,8 +27,6 @@ SettingsContentBase {
headerComponents: [
StatusButton {
implicitHeight: 38
size: StatusBaseButton.Size.Normal
text: qsTr("Send contact request to chat key")
onClicked: {
sendContactRequest.open()
@ -54,8 +52,8 @@ SettingsContentBase {
store: ({contactsStore: root.contactsStore})
isProfile: true
onOpenProfileClicked: function (pubkey, state) {
Global.openProfilePopup(pubkey, null, state)
onOpenProfileClicked: function (pubkey) {
Global.openProfilePopup(pubkey, null)
}
onCreateOneToOneChat: function (communityId, chatId, ensName) {
@ -196,6 +194,10 @@ SettingsContentBase {
contactsModel: root.contactsStore.receivedContactRequestsModel
panelUsage: Constants.contactsPanelUsage.receivedContactRequest
onSendMessageActionTriggered: {
root.contactsStore.joinPrivateChat(publicKey)
}
onContactRequestAccepted: {
root.contactsStore.acceptContactRequest(publicKey)
}
@ -205,20 +207,7 @@ SettingsContentBase {
}
onShowVerificationRequest: {
try {
let request = root.contactsStore.getVerificationDetailsFromAsJson(publicKey)
Global.openPopup(contactVerificationRequestPopupComponent, {
senderPublicKey: request.from,
senderDisplayName: request.displayName,
senderIcon: request.icon,
challengeText: request.challenge,
responseText: request.response,
messageTimestamp: request.requestedAt,
responseTimestamp: request.repliedAt
})
} catch (e) {
console.error("Error getting or parsing verification data", e)
}
Global.openIncomingIDRequestPopup(publicKey)
}
}
@ -311,41 +300,6 @@ SettingsContentBase {
}
}
// TODO: Make BlockContactConfirmationDialog a dynamic component on a future refactor
BlockContactConfirmationDialog {
id: blockContactConfirmationDialog
onBlockButtonClicked: {
root.contactsStore.blockContact(blockContactConfirmationDialog.contactAddress)
blockContactConfirmationDialog.close()
}
}
// TODO: Make ConfirmationDialog a dynamic component on a future refactor
ConfirmationDialog {
id: removeContactConfirmationDialog
header.title: qsTr("Remove contact")
confirmationText: qsTr("Are you sure you want to remove this contact?")
onConfirmButtonClicked: {
if (Utils.getContactDetailsAsJson(removeContactConfirmationDialog.value).isAdded) {
root.contactsStore.removeContact(removeContactConfirmationDialog.value);
}
removeContactConfirmationDialog.close()
}
}
Component {
id: contactVerificationRequestPopupComponent
ContactVerificationRequestPopup {
onResponseSent: {
root.contactsStore.acceptVerificationRequest(senderPublicKey, response)
}
onVerificationRefused: {
root.contactsStore.declineVerificationRequest(senderPublicKey)
}
}
}
Loader {
id: sendContactRequest
width: parent.width

View File

@ -200,7 +200,7 @@ SettingsContentBase {
Connections {
target: Global
onSettingsLoaded: {
function onSettingsLoaded() {
generalColumn.populatePreviewableSites()
}
}

View File

@ -24,6 +24,7 @@ SettingsContentBase {
property WalletStore walletStore
property ProfileStore profileStore
property PrivacyStore privacyStore
property ContactsStore contactsStore
titleRowComponentLoader.sourceComponent: StatusButton {
objectName: "profileSettingsChangePasswordButton"
@ -35,8 +36,14 @@ SettingsContentBase {
dirty: settingsView.dirty
saveChangesButtonEnabled: settingsView.valid
onResetChangesClicked: settingsView.reset()
onSaveChangesClicked: settingsView.save()
onResetChangesClicked: {
settingsView.reset()
profilePreview.reload()
}
onSaveChangesClicked: {
settingsView.save()
profilePreview.reload()
}
ColumnLayout {
id: layout
@ -73,6 +80,7 @@ SettingsContentBase {
id: profilePreview
Layout.fillWidth: true
profileStore: root.profileStore
contactsStore: root.contactsStore
}
}

View File

@ -58,7 +58,7 @@ SettingsContentBase {
StatusListItem {
property string lowerCaseSearchString: searchBox.text.toLowerCase()
width: parent.width
width: ListView.view.width
height: visible ? implicitHeight : 0
visible: lowerCaseSearchString === "" ||
model.itemId.toLowerCase().includes(lowerCaseSearchString) ||

View File

@ -1,4 +1,4 @@
import QtQuick 2.13
import QtQuick 2.14
import QtGraphicalEffects 1.14
import shared.views 1.0 as SharedViews
@ -7,6 +7,7 @@ import StatusQ.Core.Theme 0.1
Item {
property alias profileStore: profilePreview.profileStore
property alias contactsStore: profilePreview.contactsStore
implicitHeight: profilePreview.implicitHeight
+ profilePreview.anchors.topMargin
@ -16,10 +17,15 @@ Item {
+ profilePreview.anchors.leftMargin
+ profilePreview.anchors.rightMargin
SharedViews.ProfileView {
function reload() {
profilePreview.reload()
}
SharedViews.ProfileDialogView {
id: profilePreview
anchors.fill: parent
anchors.margins: 24
readOnly: true
}
DropShadow {

View File

@ -181,7 +181,6 @@ ColumnLayout {
visible: communitiesRepeater.count == 0
width: parent.width
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 15
color: Theme.palette.directColor1
text: qsTr("You haven't joined any communities yet")
}
@ -204,7 +203,6 @@ ColumnLayout {
visible: accountsRepeater.count == 0
width: parent.width
horizontalAlignment: Text.AlignHCenter
font.pixelSize: 15
color: Theme.palette.directColor1
text: qsTr("You don't have any wallet accounts yet")
}
@ -233,5 +231,4 @@ ColumnLayout {
}
}
}
}

View File

@ -52,7 +52,6 @@ Item {
}
ColumnLayout {
anchors.fill: parent
WalletHeader {
Layout.fillWidth: true
locale: RootStore.locale

View File

@ -84,13 +84,19 @@ Item {
})
return downloadPage
}
onOpenProfilePopupRequested: {
var popup = profilePopupComponent.createObject(appMain);
if (parentPopup) {
popup.parentPopup = parentPopup;
Global.openPopup(profilePopupComponent, {publicKey: publicKey, parentPopup: parentPopup})
Global.profilePopupOpened = true
}
popup.openPopup(publicKey, state);
Global.profilePopupOpened = true;
onOpenNicknamePopupRequested: {
Global.openPopup(nicknamePopupComponent, {publicKey: publicKey, nickname: nickname, "header.subTitle": subtitle})
}
onBlockContactRequested: {
Global.openPopup(blockContactConfirmationComponent, {contactName: contactName, contactAddress: publicKey})
}
onUnblockContactRequested: {
Global.openPopup(unblockContactConfirmationComponent, {contactName: contactName, contactAddress: publicKey})
}
onOpenActivityCenterPopupRequested: {
@ -106,30 +112,32 @@ Item {
onDisplayToastMessage: {
appMain.rootStore.mainModuleInst.displayEphemeralNotification(title, subTitle, icon, loading, ephNotifType, url);
}
onOpenEditDisplayNamePopup: {
var popup = displayNamePopupComponent.createObject(appMain)
popup.open()
}
onOpenEditDisplayNamePopup: Global.openPopup(displayNamePopupComponent)
}
function changeAppSectionBySectionId(sectionId) {
mainModule.setActiveSectionById(sectionId)
}
property Component backupSeedModalComponent: BackupSeedModal {
id: backupSeedModal
Component {
id: backupSeedModalComponent
BackupSeedModal {
anchors.centerIn: parent
privacyStore: appMain.rootStore.profileSectionStore.privacyStore
onClosed: destroy()
}
}
property Component displayNamePopupComponent: DisplayNamePopup {
Component {
id: displayNamePopupComponent
DisplayNamePopup {
anchors.centerIn: parent
profileStore: appMain.rootStore.profileSectionStore.profileStore
onClosed: {
destroy()
}
}
}
Component {
id: downloadPageComponent
@ -164,21 +172,24 @@ Item {
}
}
property Component profilePopupComponent: ProfilePopup {
Component {
id: profilePopupComponent
ProfileDialog {
id: profilePopup
anchors.centerIn: parent
profileStore: appMain.rootStore.profileSectionStore.profileStore
contactsStore: appMain.rootStore.profileSectionStore.contactsStore
onClosed: {
if (profilePopup.parentPopup) {
profilePopup.parentPopup.close();
profilePopup.parentPopup.close()
}
Global.profilePopupOpened = false
destroy()
}
Global.profilePopupOpened = false;
destroy();
}
}
property Component changeProfilePicComponent: Component {
Component {
id: changeProfilePicComponent
ImageCropWorkflow {
title: qsTr("Profile Picture")
acceptButtonText: qsTr("Make this my Profile Pic")
@ -636,7 +647,6 @@ Item {
}
}
}
}
Item {
@ -711,8 +721,6 @@ Item {
ChatLayout {
id: chatLayoutContainer
Layout.fillWidth: true
Layout.fillHeight: true
chatView.emojiPopup: statusEmojiPopup
@ -955,6 +963,44 @@ Item {
}
}
Component {
id: nicknamePopupComponent
NicknamePopup {
onEditDone: {
if (nickname !== newNickname) {
appMain.rootStore.contactStore.changeContactNickname(publicKey, newNickname)
Global.nickNameChanged(publicKey, newNickname)
}
close()
}
onClosed: destroy()
}
}
Component {
id: unblockContactConfirmationComponent
UnblockContactConfirmationDialog {
onUnblockButtonClicked: {
appMain.rootStore.contactStore.unblockContact(contactAddress)
Global.contactUnblocked(contactAddress)
close()
}
onClosed: destroy()
}
}
Component {
id: blockContactConfirmationComponent
BlockContactConfirmationDialog {
onBlockButtonClicked: {
appMain.rootStore.contactStore.blockContact(contactAddress)
Global.contactBlocked(contactAddress)
close()
}
onClosed: destroy()
}
}
// Add SendModal here as it is used by the Wallet as well as the Browser
Loader {
id: sendModal
@ -1183,8 +1229,8 @@ Item {
} catch (e) {
console.error('Could not parse the whitelist for sites', e)
}
Global.privacyModuleInst = appMain.rootStore.profileSectionStore.privacyStore.privacyModule
Global.settingsHasLoaded();
Global.errorSound = errorSound;
}
Loader {

View File

@ -44,6 +44,7 @@ Item {
onItemClicked: {
appSearch.store.setSearchLocation(firstLevelItemValue, secondLevelItemValue)
searchPopup.forceActiveFocus()
if(searchPopup.searchText !== "")
searchMessages(searchPopup.searchText)
}

View File

@ -15,7 +15,7 @@ Item {
property string ensAsyncValidationError: qsTr("ENS Username not found")
property alias input: contactFieldAndList.chatKey
property string selectedAddress
property var isValid: false
property bool isValid: false
property alias isPending: contactFieldAndList.loading
property bool isResolvedAddress: false
property int parentWidth

View File

@ -2,8 +2,6 @@ import QtQuick 2.13
import StatusQ.Controls 0.1
import shared.stores 1.0
import utils 1.0
StatusRoundButton {
@ -32,16 +30,6 @@ StatusRoundButton {
id: toolTip
text: qsTr("Copied!")
orientation: tooltipUnder ? StatusToolTip.Orientation.Bottom: StatusToolTip.Orientation.Top
}
Timer {
id: hideTimer
interval: 2000
running: toolTip.visible
onTriggered: {
toolTip.visible = false;
}
timeout: 2000
}
}

View File

@ -4,12 +4,12 @@ import StatusQ.Core.Utils 0.1 as StatusQUtils
import StatusQ.Components 0.1
import utils 1.0
import shared.panels 1.0
Item {
id: root
property bool compact: false
property bool oneRow
property string publicKey
readonly property real size: compact ? 10 : 15
@ -20,8 +20,8 @@ Item {
Grid {
id: positioner
rows: 2
columnSpacing: 0.2
rows: root.oneRow ? 1 : 2
columnSpacing: root.oneRow ? 4 : 2
rowSpacing: root.compact ? 4 : 6
Repeater {

View File

@ -140,24 +140,11 @@ Item {
}
}
Loader {
sourceComponent: SVGImage {
height: compact ? 10 : 16
width: compact ? 10 : 16
source: Style.svg("contact")
}
active: isContact && !root.isCurrentUser
visible: active
}
Loader {
sourceComponent: VerificationLabel {
trustStatus: root.trustStatus
height: compact ? 10 : 16
width: compact ? 10 : 16
}
active: root.trustStatus !== Constants.trustStatus.unknown && !root.isCurrentUser
visible: active
StatusContactVerificationIcons {
Layout.alignment: Qt.AlignVCenter
visible: !root.isCurrentUser
isContact: root.isContact
trustIndicator: root.trustStatus
}
Loader {

View File

@ -5,6 +5,6 @@ import StatusQ.Popups 0.1
StatusMenuItem {
property bool muted: false
text: !muted ? qsTr("Mute chat") : qsTr("Unmute chat")
text: muted ? qsTr("Unmute Chat") : qsTr("Mute Chat")
icon.name: "notification"
}

View File

@ -3,6 +3,6 @@ import QtQuick 2.14
import StatusQ.Popups 0.1
StatusMenuItem {
text: qsTr("Send message")
text: qsTr("Send Message")
icon.name: "chat"
}

View File

@ -10,26 +10,16 @@ import shared.controls.chat.menuItems 1.0
Row {
id: root
height: declineBtn.height
spacing: Style.current.halfPadding
property alias menuButton: menuButton
signal acceptClicked()
signal declineClicked()
signal blockClicked()
signal profileClicked()
height: acceptBtn.height
spacing: Style.current.halfPadding
StatusFlatRoundButton {
id: acceptBtn
width: 32
height: 32
anchors.verticalCenter: parent.verticalCenter
icon.name: "checkmark-circle"
icon.color: Style.current.success
backgroundHoverColor: Utils.setColorAlpha(Style.current.success, 0.1)
onClicked: root.acceptClicked()
}
StatusFlatRoundButton {
id: declineBtn
width: 32
@ -41,6 +31,16 @@ Row {
onClicked: root.declineClicked()
}
StatusFlatRoundButton {
id: acceptBtn
width: 32
height: 32
anchors.verticalCenter: parent.verticalCenter
icon.name: "checkmark-circle"
icon.color: Style.current.success
backgroundHoverColor: Utils.setColorAlpha(Style.current.success, 0.1)
onClicked: root.acceptClicked()
}
StatusFlatRoundButton {
id: menuButton

View File

@ -55,7 +55,7 @@ Rectangle {
Connections {
enabled: !!mainModule
target: enabled ? mainModule : undefined
onOnlineStatusChanged: {
function onOnlineStatusChanged(connected) {
if (connected && root.state !== "ready" &&
root.visible &&
root.source &&

View File

@ -8,9 +8,6 @@ import StatusQ.Core.Theme 0.1
import utils 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
Item {
id: root
@ -29,7 +26,7 @@ Item {
signal clicked()
signal closeClicked()
signal showStarted()
signal showFinihsed()
signal showFinished()
signal hideStarted()
signal hideFinished()
@ -73,7 +70,7 @@ Item {
root.showStarted()
}
onFinished: {
root.showFinihsed()
root.showFinished()
}
}
@ -134,7 +131,6 @@ Item {
text: root.text
font.pixelSize: 13
font.weight: Font.Medium
anchors.verticalCenter: parent.verticalCenter
color: Theme.palette.indirectColor1
linkColor: color
onLinkActivated: root.linkActivated(link)

View File

@ -1,4 +1,5 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import QtQml.Models 2.14
@ -10,7 +11,7 @@ import "../controls"
import SortFilterProxyModel 0.2
Item {
Control {
id: root
property string bio
@ -18,9 +19,6 @@ Item {
onUserSocialLinksJsonChanged: d.buildSocialLinksModel()
implicitWidth: layout.implicitWidth
implicitHeight: layout.implicitHeight
QtObject {
id: d
@ -83,11 +81,7 @@ Item {
]
}
ColumnLayout {
id: layout
anchors.fill: parent
contentItem: ColumnLayout {
spacing: 20
StatusBaseText {
@ -95,15 +89,15 @@ Item {
visible: text !== ""
text: root.bio
wrapMode: Text.Wrap
font.weight: Font.Medium
lineHeight: 1.2
}
Flow {
StatusCenteredFlow {
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: 10
Layout.rightMargin: 10
spacing: 16
spacing: Style.current.halfPadding
visible: repeater.count > 0
Repeater {

View File

@ -15,7 +15,6 @@ ModalPopup {
height: 237
width: 400
property Popup parentPopup
property string contactAddress: ""
property string contactName: ""

View File

@ -1,6 +1,5 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Dialogs 1.3
import utils 1.0
@ -37,9 +36,6 @@ StatusModal {
onOpened: {
verificationResponse.input.edit.forceActiveFocus(Qt.MouseFocusReason)
}
onClosed: {
root.destroy();
}
contentItem: Item {
anchors.left: parent.left
@ -69,6 +65,7 @@ StatusModal {
messageTimestamp: root.messageTimestamp
senderId: root.senderPublicKey
senderDisplayName: root.senderDisplayName
senderIsEnsVerified: Utils.isEnsVerified(root.senderPublicKey)
senderIcon: root.senderIcon
messageText: root.challengeText
messageContentType: Constants.messageContentType.messageType
@ -100,7 +97,8 @@ StatusModal {
shouldRepeatHeader: true
messageTimestamp: root.responseTimestamp
senderId: userProfile.pubKey
senderDisplayName: userProfile.name
senderDisplayName: userProfile.displayName
senderIsEnsVerified: !!userProfile.ensName
senderIcon: userProfile.icon
messageText: root.responseText
messageContentType: Constants.messageContentType.messageType
@ -115,7 +113,7 @@ StatusModal {
wrapMode: Text.WordWrap
anchors.top: responseMessage.bottom
anchors.topMargin: 58
text: qsTr("You're answer has been sent to %1.").arg(root.senderDisplayName)
text: qsTr("Your answer has been sent to %1.").arg(root.senderDisplayName)
font.pixelSize: 13
horizontalAlignment: Text.AlignHCenter
}

View File

@ -47,6 +47,6 @@ StatusModal {
}
]
onOpened: { displayNameInput.input.forceActiveFocus(Qt.MouseFocusReason) }
onOpened: { displayNameInput.input.edit.forceActiveFocus() }
}

View File

@ -18,15 +18,18 @@ StatusModal {
anchors.centerIn: parent
header.title: qsTr("Nickname")
header.subTitleElide: Text.ElideMiddle
/*required*/ property string publicKey
property string nickname
property alias nickname: nicknameInput.text
readonly property int nicknameLength: nicknameInput.text.length
readonly property int maxNicknameLength: 32
signal editDone(string newNickname)
onOpened: {
nicknameInput.forceActiveFocus(Qt.MouseFocusReason);
nicknameInput.input.edit.forceActiveFocus()
}
contentItem: Item {
@ -51,9 +54,9 @@ StatusModal {
StatusInput {
id: nicknameInput
placeholderText: qsTr("Nickname")
input.clearable: true
width: parent.width
text: popup.nickname
charLimit: maxNicknameLength
validationMode: StatusInput.ValidationMode.IgnoreInvalidInput
validators: [

View File

@ -0,0 +1,110 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import QtQml.Models 2.14
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 {
id: root
/* required*/ property string userPublicKey
property int verificationStatus
property string verificationChallenge
property string verificationResponse
property string verificationResponseDisplayName
property string verificationResponseIcon
property string verificationRequestedAt
property string verificationRepliedAt
signal verificationRequestCanceled(string userPublicKey)
signal untrustworthyVerified(string userPublicKey)
signal trustedVerified(string userPublicKey)
title: qsTr("Verify %1's Identity").arg(root.verificationResponseDisplayName)
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()
}
}
}
rightButtons: ObjectModel {
StatusButton {
text: qsTr("Mark Untrustworthy")
enabled: root.verificationResponse !== ""
type: StatusBaseButton.Type.Danger
onClicked: {
root.untrustworthyVerified(root.userPublicKey)
root.close()
}
}
StatusButton {
text: qsTr("Confirm Identity")
enabled: root.verificationResponse !== ""
type: StatusBaseButton.Type.Primary
onClicked: {
root.trustedVerified(root.userPublicKey)
root.close()
}
}
}
}
contentItem: ColumnLayout {
MessageView {
id: challengeMessage
Layout.fillWidth: true
isMessage: true
shouldRepeatHeader: true
messageTimestamp: root.verificationRequestedAt
senderId: userProfile.pubKey
senderDisplayName: userProfile.name
senderIcon: userProfile.icon
senderIsEnsVerified: !!userProfile.ensName
messageText: root.verificationChallenge
messageContentType: Constants.messageContentType.messageType
placeholderMessage: true
}
MessageView {
id: responseMessage
visible: root.verificationResponse !== ""
Layout.fillWidth: true
isMessage: true
shouldRepeatHeader: true
messageTimestamp: root.verificationRepliedAt
senderId: root.userPublicKey
senderDisplayName: root.verificationResponseDisplayName
senderIcon: root.verificationResponseIcon
senderIsEnsVerified: Utils.isEnsVerified(root.userPublicKey)
messageText: root.verificationResponse
messageContentType: Constants.messageContentType.messageType
placeholderMessage: true
}
StatusBaseText {
id: waitingForText
visible: !root.verificationResponse
text: qsTr("Waiting for %1's response...").arg(root.verificationResponseDisplayName)
font.pixelSize: Style.current.additionalTextSize
horizontalAlignment : Text.AlignHCenter
Layout.alignment: Qt.AlignHCenter
Layout.fillWidth: true
wrapMode: Text.WordWrap
color: Theme.palette.baseColor1
}
}
}

View File

@ -0,0 +1,29 @@
import QtQuick 2.14
import StatusQ.Popups.Dialog 0.1
import shared.views 1.0
StatusDialog {
id: root
property var parentPopup
property string publicKey
property var profileStore
property var contactsStore
width: 640
padding: 0
header: null
footer: null
contentItem: ProfileDialogView {
publicKey: root.publicKey
profileStore: root.profileStore
contactsStore: root.contactsStore
onCloseRequested: root.close()
}
}

View File

@ -1,474 +0,0 @@
import QtQuick 2.13
import QtQuick.Controls 2.13
import QtQuick.Layouts 1.13
import QtQml.Models 2.14
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 1.0 as SharedViews
import shared.controls.chat 1.0
import shared.panels 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups.Dialog 0.1
StatusDialog {
id: popup
property Popup parentPopup
property var profileStore
property var contactsStore
property string userPublicKey: ""
property string userDisplayName: ""
property string userName: ""
property string userNickname: ""
property string userEnsName: ""
property string userIcon: ""
property string userBio: ""
property string userSocialLinks: ""
property int userTrustStatus: Constants.trustStatus.unknown
property int outgoingVerificationStatus: Constants.verificationStatus.unverified
property int incomingVerificationStatus: Constants.verificationStatus.unverified
property string text: ""
property string challenge: ""
property string response: ""
property bool userIsEnsVerified: false
property bool userIsBlocked: false
property bool userIsUntrustworthy: false
property bool userTrustIsUnknown: false
property bool isCurrentUser: false
property bool isAddedContact: 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: ""
signal blockButtonClicked(name: string, address: string)
signal unblockButtonClicked(name: string, address: string)
signal removeButtonClicked(address: string)
signal contactUnblocked(publicKey: string)
signal contactBlocked(publicKey: string)
function openPopup(publicKey, state = "") {
// All this should be improved more, but for now we leave it like this.
const contactDetails = Utils.getContactDetailsAsJson(publicKey);
isCurrentUser = popup.profileStore.pubkey === publicKey;
userPublicKey = publicKey;
userDisplayName = isCurrentUser ? Qt.binding(() => { return popup.profileStore.displayName }) : contactDetails.displayName;
userName = contactDetails.alias;
userNickname = contactDetails.localNickname;
userEnsName = contactDetails.name;
userIcon = contactDetails.largeImage;
userBio = contactDetails.bio;
userSocialLinks = contactDetails.socialLinks;
userIsEnsVerified = contactDetails.ensVerified;
userIsBlocked = contactDetails.isBlocked;
isAddedContact = contactDetails.isAdded;
isContact = contactDetails.isContact
userTrustStatus = contactDetails.trustStatus
userTrustIsUnknown = contactDetails.trustStatus === Constants.trustStatus.unknown
userIsUntrustworthy = contactDetails.trustStatus === Constants.trustStatus.untrustworthy
outgoingVerificationStatus = contactDetails.verificationStatus
incomingVerificationStatus = contactDetails.incomingVerificationStatus
isVerificationSent = outgoingVerificationStatus !== Constants.verificationStatus.unverified
if (isContact && popup.contactsStore.hasReceivedVerificationRequestFrom(publicKey)) {
popup.hasReceivedVerificationRequest = true
}
if(isContact && isVerificationSent) {
let verificationDetails = popup.contactsStore.getSentVerificationDetailsAsJson(publicKey);
outgoingVerificationStatus = verificationDetails.requestStatus;
verificationChallenge = verificationDetails.challenge;
verificationResponse = verificationDetails.response;
verificationResponseDisplayName = verificationDetails.displayName;
verificationResponseIcon = verificationDetails.icon;
verificationRequestedAt = verificationDetails.requestedAt;
verificationRepliedAt = verificationDetails.repliedAt;
}
isTrusted = outgoingVerificationStatus === Constants.verificationStatus.trusted
|| incomingVerificationStatus === Constants.verificationStatus.trusted
isVerified = outgoingVerificationStatus === Constants.verificationStatus.verified
text = ""; // this is most likely unneeded
popup.open();
if (state === Constants.profilePopupStates.openNickname) {
profileView.nicknamePopup.open();
} else if (state === Constants.profilePopupStates.contactRequest) {
d.openContactRequestPopup()
} else if (state === Constants.profilePopupStates.blockUser) {
blockUser();
} else if (state === Constants.profilePopupStates.unblockUser) {
unblockUser();
} else if (state === Constants.profilePopupStates.verifyIdentity) {
showVerifyIdentitySection = true;
} else if (state === Constants.profilePopupStates.respondToPendingRequest) {
popup.openPendingRequestPopup()
} else if (state === Constants.profilePopupStates.showVerificationPendingSection) {
popup.showVerificationPendingSection = true
profileView.wizardAnimation.running = true
}
}
function blockUser() {
profileView.blockContactConfirmationDialog.contactName = userDisplayName;
profileView.blockContactConfirmationDialog.contactAddress = userPublicKey;
profileView.blockContactConfirmationDialog.open();
}
function unblockUser() {
profileView.unblockContactConfirmationDialog.contactName = userDisplayName;
profileView.unblockContactConfirmationDialog.contactAddress = userPublicKey;
profileView.unblockContactConfirmationDialog.open();
}
function openPendingRequestPopup() {
try {
let request = popup.contactsStore.getVerificationDetailsFromAsJson(popup.userPublicKey)
Global.openPopup(contactVerificationRequestPopupComponent, {
senderPublicKey: request.from,
senderDisplayName: request.displayName,
senderIcon: request.icon,
challengeText: request.challenge,
responseText: request.response,
messageTimestamp: request.requestedAt,
responseTimestamp: request.repliedAt
})
} catch (e) {
console.error("Error getting or parsing verification data", e)
}
}
QtObject {
id: d
function openContactRequestPopup() {
let contactRequestPopup = Global.openContactRequestPopup(popup.userPublicKey)
contactRequestPopup.closed.connect(popup.close)
}
}
width: 700
padding: 8
header: StatusDialogHeader {
id: dialogHeader
headline.title: {
if(showVerifyIdentitySection || showVerificationPendingSection){
return qsTr("Verify %1's Identity").arg(userDisplayName)
}
return popup.isCurrentUser ? qsTr("My Profile") :
qsTr("%1's Profile").arg(userDisplayName)
}
headline.subtitle: popup.isCurrentUser ? "" : Utils.getElidedCompressedPk(userPublicKey)
actions {
customButtons: ObjectModel {
StatusFlatRoundButton {
type: StatusFlatRoundButton.Type.Secondary
width: 32
height: 32
icon.width: 20
icon.height: 20
icon.name: "qr"
onClicked: profileView.qrCodePopup.open()
}
}
closeButton.onClicked: popup.close()
}
}
footer: StatusDialogFooter {
visible: !popup.isCurrentUser
leftButtons: ObjectModel {
StatusButton {
text: qsTr("Cancel verification")
visible: !isVerified && isContact && isVerificationSent && showVerificationPendingSection
onClicked: {
popup.contactsStore.cancelVerificationRequest(userPublicKey);
popup.close()
}
}
}
rightButtons: ObjectModel {
StatusFlatButton {
text: userIsBlocked ?
qsTr("Unblock User") :
qsTr("Block User")
type: StatusBaseButton.Type.Danger
visible: !isAddedContact
onClicked: userIsBlocked ? unblockUser() : blockUser()
}
StatusFlatButton {
visible: !showRemoveVerified && !showIdentityVerified && !showVerifyIdentitySection && !showVerificationPendingSection && !userIsBlocked && isAddedContact
type: StatusBaseButton.Type.Danger
text: qsTr('Remove Contact')
onClicked: {
profileView.removeContactConfirmationDialog.parentPopup = popup;
profileView.removeContactConfirmationDialog.open();
}
}
StatusButton {
text: qsTr("Send Contact Request")
visible: !userIsBlocked && !isAddedContact
onClicked: d.openContactRequestPopup()
}
StatusButton {
text: qsTr("Mark Untrustworthy")
visible: !showIdentityVerifiedUntrustworthy && !showIdentityVerified && !showVerifyIdentitySection && userTrustIsUnknown
enabled: !showVerificationPendingSection || verificationResponse !== ""
type: StatusBaseButton.Type.Danger
onClicked: {
if (showVerificationPendingSection) {
popup.showIdentityVerified = false;
popup.showIdentityVerifiedUntrustworthy = true;
popup.showVerificationPendingSection = false;
popup.showVerifyIdentitySection = false;
profileView.stepsListModel.setProperty(2, "stepCompleted", true);
popup.contactsStore.verifiedUntrustworthy(userPublicKey);
} else {
popup.contactsStore.markUntrustworthy(userPublicKey);
popup.close();
}
}
}
StatusButton {
text: qsTr("Remove 'Identity Verified' status")
visible: isTrusted && !showIdentityVerified && !showRemoveVerified
type: StatusBaseButton.Type.Danger
onClicked: {
showRemoveVerified = true
}
}
StatusButton {
text: qsTr("No")
visible: showRemoveVerified
type: StatusBaseButton.Type.Danger
onClicked: {
showRemoveVerified = false
}
}
StatusButton {
text: qsTr("Yes")
visible: showRemoveVerified
onClicked: {
popup.contactsStore.removeTrustStatus(userPublicKey);
popup.close();
}
}
StatusButton {
text: qsTr("Remove Untrustworthy Mark")
visible: userIsUntrustworthy
onClicked: {
popup.contactsStore.removeTrustStatus(userPublicKey);
popup.close();
}
}
StatusButton {
text: qsTr("Verify Identity")
visible: !showIdentityVerifiedUntrustworthy && !showIdentityVerified &&
!showVerifyIdentitySection && isContact && !isVerificationSent
&& !hasReceivedVerificationRequest
onClicked: {
popup.showVerifyIdentitySection = true
}
}
StatusButton {
text: qsTr("Verify Identity pending...")
visible: (!showIdentityVerifiedUntrustworthy && !showIdentityVerified && !isTrusted
&& isContact && isVerificationSent && !showVerificationPendingSection) ||
(hasReceivedVerificationRequest && !isTrusted)
onClicked: {
if (hasReceivedVerificationRequest) {
popup.openPendingRequestPopup()
} else {
popup.showVerificationPendingSection = true
profileView.wizardAnimation.running = true
}
}
}
StatusButton {
text: qsTr("Send verification request")
visible: showVerifyIdentitySection && isContact && !isVerificationSent
onClicked: {
popup.contactsStore.sendVerificationRequest(userPublicKey, Utils.escapeHtml(profileView.challengeTxt.input.text));
profileView.stepsListModel.setProperty(1, "stepCompleted", true);
Global.displayToastMessage(qsTr("Verification request sent"),
"",
"checkmark-circle",
false,
Constants.ephemeralNotificationType.normal,
"");
popup.close();
}
}
StatusButton {
text: qsTr("Confirm Identity")
visible: isContact && isVerificationSent && !isTrusted && showVerificationPendingSection
enabled: verificationChallenge !== "" && verificationResponse !== ""
onClicked: {
popup.showIdentityVerified = true;
popup.showIdentityVerifiedUntrustworthy = false;
popup.showVerificationPendingSection = false;
popup.showVerifyIdentitySection = false;
profileView.stepsListModel.setProperty(2, "stepCompleted", true);
popup.contactsStore.verifiedTrusted(userPublicKey);
popup.isTrusted = true
}
}
StatusButton {
visible: showIdentityVerified || showIdentityVerifiedUntrustworthy
text: qsTr("Rename")
onClicked: {
profileView.nicknamePopup.open()
}
}
StatusButton {
visible: showIdentityVerified || showIdentityVerifiedUntrustworthy
text: qsTr("Close")
onClicked: {
popup.close();
}
}
}
}
StatusScrollView {
id: scrollView
anchors.fill: parent
padding: 0
SharedViews.ProfileView {
id: profileView
width: scrollView.availableWidth
profileStore: popup.profileStore
contactsStore: popup.contactsStore
userPublicKey: popup.userPublicKey
userDisplayName: popup.userDisplayName
userName: popup.userName
userNickname: popup.userNickname
userEnsName: popup.userEnsName
userIcon: popup.userIcon
userBio: popup.userBio
userSocialLinks: popup.userSocialLinks
userIsEnsVerified: popup.userIsEnsVerified
userIsBlocked: popup.userIsBlocked
isAddedContact: popup.isAddedContact
isCurrentUser: popup.isCurrentUser
isContact: popup.isContact
isVerificationSent: popup.isVerificationSent
isVerified: popup.isVerified
isTrusted: popup.isTrusted
hasReceivedVerificationRequest: popup.hasReceivedVerificationRequest
userTrustStatus: popup.userTrustStatus
outgoingVerificationStatus: popup.outgoingVerificationStatus
showVerifyIdentitySection: popup.showVerifyIdentitySection
showVerificationPendingSection: popup.showVerificationPendingSection
showIdentityVerified: popup.showIdentityVerified
showIdentityVerifiedUntrustworthy: popup.showIdentityVerifiedUntrustworthy
challenge: popup.challenge
response: popup.response
userIsUntrustworthy: popup.userIsUntrustworthy
userTrustIsUnknown: popup.userTrustIsUnknown
verificationChallenge: popup.verificationChallenge
verificationResponse: popup.verificationResponse
verificationResponseDisplayName: popup.verificationResponseDisplayName
verificationResponseIcon: popup.verificationResponseIcon
verificationRequestedAt: popup.verificationRequestedAt
verificationRepliedAt: popup.verificationRepliedAt
onContactUnblocked: {
popup.close()
popup.contactUnblocked(publicKey)
}
onContactBlocked: {
popup.close()
popup.contactBlocked(publicKey)
}
onContactAdded: {
popup.close()
popup.contactAdded(publicKey)
}
onContactRemoved: {
popup.close()
}
onNicknameEdited: {
popup.close()
}
}
}
Component {
id: contactVerificationRequestPopupComponent
ContactVerificationRequestPopup {
onResponseSent: {
popup.contactsStore.acceptVerificationRequest(senderPublicKey, response)
}
onVerificationRefused: {
popup.contactsStore.declineVerificationRequest(senderPublicKey)
}
}
}
}

View File

@ -19,9 +19,12 @@ StatusModal {
property string userIcon: ""
property bool userIsEnsVerified
property string challengeText: qsTr("Say who you are / why you want to become a contact...")
property string buttonText: qsTr("Send Contact Request")
signal accepted(string message)
padding: 16
padding: Style.current.padding
header.title: qsTr("Send Contact Request to %1").arg(userDisplayName)
QtObject {
@ -33,6 +36,10 @@ StatusModal {
readonly property int contentSpacing: 5
}
onAboutToShow: {
messageInput.input.edit.forceActiveFocus()
}
ColumnLayout {
id: content
anchors.fill: parent
@ -55,7 +62,7 @@ StatusModal {
id: messageInput
charLimit: d.maxMsgLength
placeholderText: qsTr("Say who you are / why you want to become a contact...")
placeholderText: root.challengeText
input.multiline: true
minimumHeight: d.msgHeight
maximumHeight: d.msgHeight
@ -71,7 +78,7 @@ StatusModal {
rightButtons: StatusButton {
enabled: messageInput.valid
text: qsTr("Send Contact Request")
text: root.buttonText
onClicked: {
root.accepted(Utils.escapeHtml(messageInput.text));
root.close();

View File

@ -15,7 +15,6 @@ ModalPopup {
height: 237
width: 400
property Popup parentPopup
property string contactAddress: ""
property string contactName: ""

View File

@ -4,6 +4,7 @@ SettingsDirtyToastMessage 1.0 SettingsDirtyToastMessage.qml
ConfirmationDialog 1.0 ConfirmationDialog.qml
CommunityIntroDialog 1.0 CommunityIntroDialog.qml
ContactVerificationRequestPopup 1.0 ContactVerificationRequestPopup.qml
OutgoingContactVerificationRequestPopup 1.0 OutgoingContactVerificationRequestPopup.qml
DownloadModal 1.0 DownloadModal.qml
DownloadPage 1.0 DownloadPage.qml
InviteFriendsPopup 1.0 InviteFriendsPopup.qml
@ -17,7 +18,7 @@ UnblockContactConfirmationDialog 1.0 UnblockContactConfirmationDialog.qml
UserStatusContextMenu 1.0 UserStatusContextMenu.qml
SignTransactionModal 1.0 SignTransactionModal.qml
SelectAccountModal 1.0 SelectAccountModal.qml
ProfilePopup 1.0 ProfilePopup.qml
ProfileDialog 1.0 ProfileDialog.qml
ImageCropWorkflow 1.0 ImageCropWorkflow.qml
ImportCommunityPopup 1.0 ImportCommunityPopup.qml
DisplayNamePopup 1.0 DisplayNamePopup.qml

View File

@ -8,7 +8,7 @@ import ".."
StatusChatImageValidator {
id: root
errorMessage: qsTr("You can only upload %1 images at a time").arg(Constants.maxUploadFiles)
errorMessage: qsTr("You can only upload %n image(s) at a time", "", Constants.maxUploadFiles)
onImagesChanged: {
root.isValid = images.length <= Constants.maxUploadFiles

View File

@ -57,7 +57,7 @@ ModalPopup {
root.close();
} catch (e) {
console.error('Error sending the transaction', e)
sendingError.text = "Error sending the transaction: " + e.message;
sendingError.text = qsTr("Error sending the transaction: %1").arg(e.message);
return sendingError.open()
}
}

View File

@ -63,8 +63,6 @@ Popup {
searchBox.input.edit.forceActiveFocus()
if (RootStore.isTenorWarningAccepted) {
RootStore.getTrendingsGifs()
} else {
confirmationPopupLoader.active = true
}
}
@ -202,7 +200,7 @@ Popup {
sourceComponent: ConfirmationPopup {
visible: true
}
active: false
active: !RootStore.isTenorWarningAccepted
}
Component {

View File

@ -45,7 +45,7 @@ Popup {
}
Connections {
target: mainModule
onOnlineStatusChanged: {
function onOnlineStatusChanged() {
root.close()
}
}

View File

@ -179,7 +179,6 @@ QtObject {
function findTokenSymbolByAddress(address) {
return walletSectionAllTokens.findTokenSymbolByAddress(address)
}
function getNameForSavedWalletAddress(address) {

View File

@ -44,7 +44,7 @@ Item {
delegate: StatusListItem {
readonly property string balance: enabledNetworkBalance // Needed for the tests
objectName: "AssetView_TokenListItem_" + symbol
width: parent.width
width: ListView.view.width
title: name
subTitle: `${enabledNetworkBalance} ${symbol}`
asset.name: symbol ? Style.png("tokens/" + symbol) : ""

View File

@ -35,7 +35,7 @@ ColumnLayout {
target: RootStore.history
onLoadingTrxHistoryChanged: function(isLoading, address) {
if (historyView.account.address.toLowerCase() === address.toLowerCase()) {
root.isLoading = isLoading
historyView.isLoading = isLoading
}
}
}

View File

@ -0,0 +1,704 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import QtGraphicalEffects 1.14
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Controls 0.1
import StatusQ.Components 0.1
import StatusQ.Popups 0.1
import StatusQ.Popups.Dialog 0.1
import StatusQ.Core.Utils 0.1 as SQUtils
import utils 1.0
import shared.controls 1.0
import shared.panels 1.0
import shared.popups 1.0
import shared.controls.chat 1.0
import shared.controls.chat.menuItems 1.0
Pane {
id: root
property bool readOnly
property string publicKey: contactsStore.myPublicKey
property var profileStore
property var contactsStore
signal closeRequested()
padding: 0
topPadding: 40
background: StatusDialogBackground {
id: background
}
QtObject {
id: d
property var contactDetails: Utils.getContactDetailsAsJson(root.publicKey)
function reload() {
contactDetails = Utils.getContactDetailsAsJson(root.publicKey)
}
readonly property bool isCurrentUser: root.profileStore.pubkey === root.publicKey
readonly property string userDisplayName: contactDetails.displayName
readonly property string userNickName: contactDetails.localNickname
readonly property string prettyEnsName: '@' + Utils.removeStatusEns(contactDetails.name)
readonly property bool isContact: contactDetails.isContact
readonly property bool isBlocked: contactDetails.isBlocked
readonly property bool isContactRequestSent: contactDetails.isAdded
readonly property bool isContactRequestReceived: contactDetails.hasAddedUs
readonly property int outgoingVerificationStatus: contactDetails.verificationStatus
readonly property int incomingVerificationStatus: contactDetails.incomingVerificationStatus
readonly property bool isVerificationRequestSent:
outgoingVerificationStatus !== Constants.verificationStatus.unverified &&
outgoingVerificationStatus !== Constants.verificationStatus.verified &&
outgoingVerificationStatus !== Constants.verificationStatus.trusted
readonly property bool isVerificationRequestReceived: d.isCurrentUser ? false : root.contactsStore.hasReceivedVerificationRequestFrom(root.publicKey)
readonly property bool isTrusted: outgoingVerificationStatus === Constants.verificationStatus.trusted ||
incomingVerificationStatus === Constants.verificationStatus.trusted
readonly property bool isVerified: outgoingVerificationStatus === Constants.verificationStatus.verified
readonly property string linkToProfile: {
let user = ""
if (d.isCurrentUser)
user = root.profileStore.ensName
else
user = contactDetails.name
if (!user)
user = root.publicKey
return Constants.userLinkPrefix + user
}
readonly property var conns: Connections {
target: Global
function onNickNameChanged(publicKey, nickname) {
if (publicKey === root.publicKey) d.reload()
}
function onContactBlocked(publicKey) {
if (publicKey === root.publicKey) d.reload()
}
function onContactUnblocked(publicKey) {
if (publicKey === root.publicKey) d.reload()
}
}
}
function reload() {
d.reload()
}
Component {
id: btnEditProfileComponent
StatusButton {
size: StatusButton.Size.Small
text: qsTr("Edit Profile")
enabled: !root.readOnly
onClicked: {
Global.changeAppSectionBySectionType(Constants.appSection.profile)
root.closeRequested()
}
}
}
Component {
id: btnSendMessageComponent
StatusButton {
size: StatusButton.Size.Small
text: qsTr("Send Message")
onClicked: {
root.contactsStore.joinPrivateChat(root.publicKey)
root.closeRequested()
}
}
}
Component {
id: btnAcceptContactRequestComponent
ColumnLayout {
spacing: Style.current.halfPadding
StatusBaseText {
color: Theme.palette.baseColor1
font.pixelSize: 13
text: qsTr("Respond to contact request")
}
AcceptRejectOptionsButtonsPanel {
menuButton.visible: false
onAcceptClicked: {
root.contactsStore.acceptContactRequest(root.publicKey)
d.reload()
}
onDeclineClicked: {
root.contactsStore.dismissContactRequest(root.publicKey)
d.reload()
}
}
}
}
Component {
id: btnSendContactRequestComponent
StatusButton {
size: StatusButton.Size.Small
text: qsTr("Send Contact Request")
onClicked: {
let contactRequestPopup = Global.openContactRequestPopup(root.publicKey)
contactRequestPopup.accepted.connect(d.reload)
}
}
}
Component {
id: btnBlockUserComponent
StatusButton {
size: StatusButton.Size.Small
type: StatusBaseButton.Type.Danger
text: qsTr("Block User")
onClicked: Global.blockContactRequested(root.publicKey, d.userDisplayName)
}
}
Component {
id: btnUnblockUserComponent
StatusButton {
size: StatusButton.Size.Small
text: qsTr("Unblock User")
onClicked: Global.unblockContactRequested(root.publicKey, d.userDisplayName)
}
}
Component {
id: txtPendingContactRequestComponent
StatusBaseText {
font.pixelSize: 13
font.weight: Font.Medium
color: Theme.palette.baseColor1
verticalAlignment: Text.AlignVCenter
text: qsTr("Contact Request Pending...")
}
}
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 {
size: StatusButton.Size.Small
text: qsTr("Respond to ID Request")
onClicked: {
let idRequestPopup = Global.openIncomingIDRequestPopup(root.publicKey)
idRequestPopup.closed.connect(d.reload)
}
}
}
ConfirmationDialog {
id: removeContactConfirmationDialog
header.title: qsTr("Remove contact '%1'").arg(d.userDisplayName)
confirmationText: qsTr("This will remove the user as a contact. Please confirm.")
onConfirmButtonClicked: {
root.contactsStore.removeContact(root.publicKey)
close()
d.reload()
}
}
ConfirmationDialog {
id: removeVerificationConfirmationDialog
header.title: qsTr("Remove contact verification")
confirmationText: qsTr("This will remove the contact's verified status. Please confirm.")
onConfirmButtonClicked: {
root.contactsStore.removeTrustStatus(root.publicKey)
close()
d.reload()
}
}
ColumnLayout {
id: column
spacing: 20
anchors {
fill: parent
leftMargin: Style.current.bigPadding
rightMargin: Style.current.bigPadding
}
RowLayout {
Layout.fillWidth: true
spacing: Style.current.halfPadding
UserImage {
Layout.alignment: Qt.AlignTop
objectName: "ProfileDialog_userImage"
name: d.userDisplayName
pubkey: root.publicKey
image: d.contactDetails.largeImage
interactive: false
imageWidth: 80
imageHeight: imageWidth
showRing: !d.contactDetails.ensVerified
}
ColumnLayout {
Layout.fillWidth: true
Layout.leftMargin: 4
Layout.alignment: Qt.AlignTop
spacing: 4
Item {
id: contactRow
Layout.fillWidth: true
Layout.preferredHeight: childrenRect.height
StatusBaseText {
id: contactName
anchors.left: parent.left
width: Math.min(implicitWidth, contactRow.width - verificationIcons.width - verificationIcons.anchors.leftMargin)
objectName: "ProfileDialog_displayName"
font.bold: true
font.pixelSize: 22
elide: Text.ElideRight
text: d.userDisplayName
}
StatusContactVerificationIcons {
id: verificationIcons
anchors.left: contactName.right
anchors.leftMargin: Style.current.halfPadding
anchors.verticalCenter: contactName.verticalCenter
objectName: "ProfileDialog_userVerificationIcons"
visible: !d.isCurrentUser
isContact: d.isContact
trustIndicator: d.contactDetails.trustStatus
tiny: false
}
}
StatusBaseText {
id: contactSecondaryName
font.pixelSize: 12
color: Theme.palette.baseColor1
text: {
let result = ""
if (d.userNickName) {
if (d.contactDetails.ensVerified)
result = d.prettyEnsName
else
result = d.contactDetails.optionalName // original display name
}
if (result)
return "(%1)".arg(result)
return ""
}
visible: text
}
EmojiHash {
objectName: "ProfileDialog_userEmojiHash"
publicKey: root.publicKey
}
}
Loader {
Layout.alignment: Qt.AlignTop
Layout.preferredHeight: menuButton.visible ? menuButton.height : -1
sourceComponent: {
// current user
if (d.isCurrentUser)
return btnEditProfileComponent
// contact request, outgoing, rejected
if (!d.isContact && d.isContactRequestSent && d.outgoingVerificationStatus === Constants.verificationStatus.declined)
return txtRejectedContactRequestComponent
// contact request, outgoing, pending
if (!d.isContact && d.isContactRequestSent)
return txtPendingContactRequestComponent
// contact request, incoming, pending
if (!d.isContact && d.isContactRequestReceived)
return btnAcceptContactRequestComponent
// contact request, incoming, rejected
if (d.isContactRequestSent && d.incomingVerificationStatus === Constants.verificationStatus.declined)
return btnBlockUserComponent
// verified contact request, incoming, pending
if (d.isContact && !d.isTrusted && d.isVerificationRequestReceived)
return btnRespondToIdRequestComponent
// block user
if (!d.isContact && !d.isBlocked &&
(d.contactDetails.trustStatus === Constants.trustStatus.untrustworthy || d.outgoingVerificationStatus === Constants.verificationStatus.declined))
return btnBlockUserComponent
// send contact request
if (!d.isContact && !d.isBlocked && !d.isContactRequestSent)
return btnSendContactRequestComponent
// blocked contact
if (d.isBlocked)
return btnUnblockUserComponent
// send message
if (d.isContact && !d.isBlocked)
return btnSendMessageComponent
console.warn("!!! UNHANDLED CONTACT ACTION BUTTON; PUBKEY", root.publicKey)
}
}
StatusFlatButton {
id: menuButton
Layout.alignment: Qt.AlignTop
Layout.preferredWidth: height
visible: !d.isCurrentUser
size: StatusBaseButton.Size.Small
horizontalPadding: 6
verticalPadding: 6
icon.name: "more"
icon.color: Theme.palette.directColor1
highlighted: moreMenu.opened
onClicked: moreMenu.popup(-moreMenu.width + width, height + 4)
StatusPopupMenu {
id: moreMenu
width: 230
SendContactRequestMenuItem {
enabled: !d.isContact && !d.isBlocked && !d.isContactRequestSent && !d.contactDetails.removed &&
d.contactDetails.trustStatus === Constants.trustStatus.untrustworthy // we have an action button otherwise
onTriggered: {
moreMenu.close()
let contactRequestPopup = Global.openContactRequestPopup(root.publicKey)
contactRequestPopup.closed.connect(d.reload)
}
}
StatusMenuItem {
text: qsTr("Verify Identity")
icon.name: "checkmark-circle"
enabled: d.isContact && !d.isBlocked &&
d.outgoingVerificationStatus === Constants.verificationStatus.unverified &&
!d.isVerificationRequestReceived
onTriggered: {
moreMenu.close()
let idRequestPopup = Global.openSendIDRequestPopup(root.publicKey)
idRequestPopup.accepted.connect(d.reload)
}
}
StatusMenuItem {
text: qsTr("ID Request Pending...")
icon.name: "checkmark-circle"
enabled: d.isContact && !d.isBlocked && !d.isTrusted && d.isVerificationRequestSent
onTriggered: {
moreMenu.close()
let idRequestPopup = Global.openOutgoingIDRequestPopup(root.publicKey)
idRequestPopup.closed.connect(d.reload)
}
}
StatusMenuItem {
text: qsTr("Rename")
icon.name: "edit_pencil"
onTriggered: {
moreMenu.close()
Global.openNicknamePopupRequested(root.publicKey, d.userNickName,
"%1 (%2)".arg(d.userDisplayName).arg(Utils.getElidedCompressedPk(root.publicKey)))
}
}
StatusMenuItem {
text: qsTr("Reverse Contact Rejection")
icon.name: "refresh"
enabled: d.contactDetails.removed
onTriggered: {
moreMenu.close()
root.contactsStore.removeContactRequestRejection(root.publicKey)
d.reload()
}
}
StatusMenuItem {
text: qsTr("Copy Link to Profile")
icon.name: "copy"
onTriggered: {
moreMenu.close()
root.profileStore.copyToClipboard(d.linkToProfile)
}
}
StatusMenuItem {
text: qsTr("Unblock User")
icon.name: "remove-circle"
enabled: d.isBlocked
onTriggered: {
moreMenu.close()
Global.unblockContactRequested(root.publicKey, d.userDisplayName)
}
}
StatusMenuSeparator {}
StatusMenuItem {
text: qsTr("Mark as Untrustworthy")
icon.name: "warning"
type: StatusMenuItem.Type.Danger
enabled: d.contactDetails.trustStatus === Constants.trustStatus.unknown
onTriggered: {
moreMenu.close()
if (d.isContact && !d.isTrusted && d.isVerificationRequestReceived)
root.contactsStore.verifiedUntrustworthy(root.publicKey)
else
root.contactsStore.markUntrustworthy(root.publicKey)
d.reload()
}
}
StatusMenuItem {
text: qsTr("Remove Untrustworthy Mark")
icon.name: "warning"
enabled: d.contactDetails.trustStatus === Constants.trustStatus.untrustworthy
onTriggered: {
moreMenu.close()
root.contactsStore.removeTrustStatus(root.publicKey)
d.reload()
}
}
StatusMenuItem {
text: qsTr("Remove Identity Verification")
icon.name: "warning"
type: StatusMenuItem.Type.Danger
enabled: d.isContact && d.isTrusted
onTriggered: {
moreMenu.close()
removeVerificationConfirmationDialog.open()
}
}
StatusMenuItem {
text: qsTr("Remove Contact")
icon.name: "remove-contact"
type: StatusMenuItem.Type.Danger
enabled: d.isContact && !d.isBlocked
onTriggered: {
moreMenu.close()
removeContactConfirmationDialog.open()
}
}
StatusMenuItem {
text: qsTr("Cancel Contact Request")
icon.name: "cancel"
type: StatusMenuItem.Type.Danger
enabled: !d.isContact && d.isContactRequestSent && !d.contactDetails.removed
onTriggered: {
moreMenu.close()
root.contactsStore.removeContact(root.publicKey)
d.reload()
}
}
StatusMenuItem {
text: qsTr("Block User")
icon.name: "cancel"
type: StatusMenuItem.Type.Danger
enabled: !d.isBlocked
onTriggered: {
moreMenu.close()
Global.blockContactRequested(root.publicKey, d.userDisplayName)
}
}
}
}
}
StatusDialogDivider {
Layout.fillWidth: true
Layout.leftMargin: -column.anchors.leftMargin
Layout.rightMargin: -column.anchors.rightMargin
Layout.topMargin: -column.spacing
Layout.bottomMargin: -column.spacing
opacity: scrollView.atYBeginning ? 0 : 1
Behavior on opacity { OpacityAnimator {} }
}
StatusScrollView {
id: scrollView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.leftMargin: -column.anchors.leftMargin
Layout.rightMargin: -column.anchors.rightMargin
Layout.topMargin: -column.spacing
padding: 0
ColumnLayout {
width: scrollView.width
spacing: 20
ProfileBioSocialsPanel {
Layout.fillWidth: true
Layout.leftMargin: column.anchors.leftMargin + Style.current.halfPadding
Layout.rightMargin: column.anchors.rightMargin + Style.current.halfPadding
bio: d.contactDetails.bio
userSocialLinksJson: d.contactDetails.socialLinks
}
GridLayout {
Layout.fillWidth: true
Layout.leftMargin: column.anchors.leftMargin
Layout.rightMargin: column.anchors.rightMargin
flow: GridLayout.TopToBottom
rowSpacing: Style.current.halfPadding
columnSpacing: Style.current.bigPadding
visible: d.isCurrentUser
enabled: visible
columns: 2
rows: 4
StatusBaseText {
Layout.fillWidth: true
text: qsTr("Link to Profile")
font.pixelSize: 13
}
StatusBaseInput {
Layout.fillWidth: true
Layout.preferredHeight: 56
leftPadding: 14
rightPadding: Style.current.halfPadding
topPadding: 0
bottomPadding: 0
placeholder.rightPadding: Style.current.halfPadding
placeholderText: d.linkToProfile
placeholderTextColor: Theme.palette.directColor1
edit.readOnly: true
rightComponent: StatusButton {
anchors.verticalCenter: parent.verticalCenter
borderColor: Theme.palette.primaryColor1
size: StatusBaseButton.Size.Tiny
text: qsTr("Copy")
onClicked: {
text = qsTr("Copied")
root.profileStore.copyToClipboard(d.linkToProfile)
}
}
}
StatusBaseText {
Layout.fillWidth: true
Layout.topMargin: Style.current.smallPadding
text: qsTr("Emoji Hash")
font.pixelSize: 13
}
StatusBaseInput {
Layout.fillWidth: true
Layout.preferredHeight: 56
leftPadding: 14
rightPadding: Style.current.halfPadding
topPadding: 0
bottomPadding: 0
edit.readOnly: true
leftComponent: EmojiHash {
publicKey: root.publicKey
oneRow: !root.readOnly
}
rightComponent: StatusButton {
anchors.verticalCenter: parent.verticalCenter
borderColor: Theme.palette.primaryColor1
size: StatusBaseButton.Size.Tiny
text: qsTr("Copy")
onClicked: {
root.profileStore.copyToClipboard(Utils.getEmojiHashAsJson(root.publicKey).join("").toString())
text = qsTr("Copied")
}
}
}
Rectangle {
Layout.rowSpan: 4
Layout.fillHeight: true
Layout.preferredWidth: height
Layout.alignment: Qt.AlignCenter
color: "transparent"
border.width: 1
border.color: Theme.palette.baseColor2
radius: Style.current.halfPadding
Image {
anchors.centerIn: parent
asynchronous: true
fillMode: Image.PreserveAspectFit
width: 170
height: width
mipmap: true
smooth: false
source: root.profileStore.getQrCodeSource(root.profileStore.pubkey)
}
}
}
StatusTabBar {
Layout.fillWidth: true
Layout.leftMargin: column.anchors.leftMargin
Layout.rightMargin: column.anchors.rightMargin
bottomPadding: -4
StatusTabButton {
leftPadding: 0
width: implicitWidth
text: qsTr("Tokens")
}
StatusTabButton {
width: implicitWidth
text: qsTr("NFTs")
}
StatusTabButton {
width: implicitWidth
text: qsTr("Communities")
}
StatusTabButton {
width: implicitWidth
text: qsTr("Accounts")
}
}
StatusDialogBackground {
Layout.fillWidth: true
Layout.topMargin: -column.spacing
Layout.preferredHeight: 300
color: Theme.palette.baseColor4
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.top: parent.top
height: parent.radius
color: parent.color
}
StatusBaseText {
anchors.centerIn: parent
color: Theme.palette.baseColor1
text: qsTr("More content to appear here soon...")
}
}
}
}
}
layer.enabled: !root.readOnly // profile preview has its own layer.effect
layer.effect: OpacityMask {
maskSource: Rectangle {
anchors.centerIn: parent
width: column.width
height: column.height
radius: background.radius
}
}
}

View File

@ -1,484 +0,0 @@
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
import shared.controls.chat 1.0
import shared.panels 1.0
import StatusQ.Core 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
import StatusQ.Components 0.1
import StatusQ.Controls 0.1
import StatusQ.Popups 0.1
import "../panels"
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 userBio: profileStore.bio
property string userSocialLinks: profileStore.socialLinksJson
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: ""
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
signal contactUnblocked(publicKey: string)
signal contactBlocked(publicKey: string)
signal contactAdded(publicKey: string)
signal contactRemoved(publicKey: string)
signal nicknameEdited(publicKey: string)
objectName: "profileView"
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);
}
}
}
}
ColumnLayout {
id: modalContent
anchors.fill: parent
Item {
Layout.fillWidth: true
implicitHeight: 32
}
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
displayNameVisible: false
displayNamePlusIconsVisible: true
pubkeyVisibleWithCopy: true
pubkeyVisible: false
imageSize: ProfileHeader.ImageSize.Middle
editImageButtonVisible: root.isCurrentUser
onEditClicked: {
if(!isCurrentUser){
nicknamePopup.open()
} else {
Global.openEditDisplayNamePopup()
}
}
}
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
}
StatusDescriptionListItem {
Layout.fillWidth: true
visible: !showVerifyIdentitySection && !showVerificationPendingSection && !showIdentityVerified
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"
iconButton.onClicked: {
globalUtils.copyToClipboard(subTitle)
tooltip.open();
}
}
StatusDescriptionListItem {
Layout.fillWidth: true
visible: !showVerifyIdentitySection && !showVerificationPendingSection && !showIdentityVerified
title: qsTr("Share Profile URL")
subTitle: {
let user = ""
if (isCurrentUser) {
user = root.profileStore.ensName !== "" ? root.profileStore.ensName : StatusQUtils.Utils.elideText(root.profileStore.pubkey, 5)
} else if (userIsEnsVerified) {
user = userEnsName
}
if (user === ""){
user = StatusQUtils.Utils.elideText(userPublicKey, 5)
}
return Constants.userLinkPrefix + user;
}
tooltip.text: qsTr("Copied to clipboard")
tooltip.timeout: 1000
asset.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();
}
}
ListModel {
id: stepsListModel
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
}
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();
}
}
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
}
Item {
height: 32
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)
}
}
}

View File

@ -106,7 +106,7 @@ StatusPopupMenu {
property var emojiReactionsReactedByUser: []
signal openProfileClicked(string publicKey, string state)
signal openProfileClicked(string publicKey)
signal pinMessage(string messageId)
signal unpinMessage(string messageId)
signal pinnedMessagesLimitReached(string messageId)
@ -238,7 +238,7 @@ StatusPopupMenu {
id: viewProfileAction
enabled: root.isProfile && !root.pinnedPopup
onTriggered: {
root.openProfileClicked(root.selectedUserPublicKey, "")
root.openProfileClicked(root.selectedUserPublicKey)
root.close()
}
}
@ -256,8 +256,7 @@ StatusPopupMenu {
enabled: root.isProfile && !root.isMe && !root.isContact
&& !root.isBlockedContact && !root.hasPendingContactRequest
onTriggered: {
root.openProfileClicked(root.selectedUserPublicKey,
Constants.profilePopupStates.contactRequest)
Global.openContactRequestPopup(root.selectedUserPublicKey)
root.close()
}
}
@ -270,8 +269,7 @@ StatusPopupMenu {
&& root.outgoingVerificationStatus === Constants.verificationStatus.unverified
&& !root.hasReceivedVerificationRequestFrom
onTriggered: {
root.openProfileClicked(root.selectedUserPublicKey,
Constants.profilePopupStates.verifyIdentity)
Global.openSendIDRequestPopup(root.selectedUserPublicKey)
root.close()
}
}
@ -288,11 +286,9 @@ StatusPopupMenu {
|| root.isVerificationRequestSent)
onTriggered: {
if (hasReceivedVerificationRequestFrom) {
root.openProfileClicked(root.selectedUserPublicKey,
Constants.profilePopupStates.respondToPendingRequest)
Global.openIncomingIDRequestPopup(root.selectedUserPublicKey)
} else if (root.isVerificationRequestSent) {
root.openProfileClicked(root.selectedUserPublicKey,
Constants.profilePopupStates.showVerificationPendingSection)
Global.openOutgoingIDRequestPopup(root.selectedUserPublicKey)
}
root.close()
@ -304,8 +300,8 @@ StatusPopupMenu {
icon.name: "edit_pencil"
enabled: root.isProfile && !root.isMe
onTriggered: {
root.openProfileClicked(root.selectedUserPublicKey,
Constants.profilePopupStates.openNickname)
Global.openNicknamePopupRequested(root.selectedUserPublicKey, d.contactDetails.localNickname,
"%1 (%2)".arg(root.selectedUserDisplayName).arg(Utils.getElidedCompressedPk(root.selectedUserPublicKey)))
root.close()
}
}
@ -314,7 +310,7 @@ StatusPopupMenu {
text: qsTr("Unblock User")
icon.name: "remove-circle"
enabled: root.isProfile && !root.isMe && root.isBlockedContact
onTriggered: root.store.contactsStore.unblockContact(root.selectedUserPublicKey)
onTriggered: Global.unblockContactRequested(root.selectedUserPublicKey, root.selectedUserDisplayName)
}
StatusMenuSeparator {
@ -344,7 +340,7 @@ StatusPopupMenu {
icon.name: "cancel"
type: StatusMenuItem.Type.Danger
enabled: root.isProfile && !root.isMe && !root.isBlockedContact
onTriggered: root.store.contactsStore.blockContact(root.selectedUserPublicKey)
onTriggered: Global.blockContactRequested(root.selectedUserPublicKey, root.selectedUserDisplayName)
}
StatusMenuItem {

View File

@ -7,7 +7,7 @@ TransactionPreview 1.0 TransactionPreview.qml
TransactionSigner 1.0 TransactionSigner.qml
TransactionStackView 1.0 TransactionStackView.qml
PasswordView 1.0 PasswordView.qml
ProfileView 1.0 ProfileView.qml
ProfileDialogView 1.0 ProfileDialogView.qml
AssetsView 1.0 AssetsView.qml
HistoryView 1.0 HistoryView.qml
AssetsDetailView 1.0 AssetsDetailView.qml

View File

@ -280,16 +280,6 @@ QtObject {
readonly property int blockedContacts: 6
}
readonly property QtObject profilePopupStates: QtObject {
readonly property string openNickname: "openNickname"
readonly property string contactRequest: "contactRequest"
readonly property string blockUser: "blockUser"
readonly property string unblockUser: "unblockUser"
readonly property string verifyIdentity: "verifyIdentity"
readonly property string showVerificationPendingSection: "showVerificationPendingSection"
readonly property string respondToPendingRequest: "respondToPendingRequest"
}
readonly property QtObject validators: QtObject {
readonly property list<StatusValidator> displayName: [
StatusMinLengthValidator {
@ -567,7 +557,7 @@ QtObject {
readonly property int maxNumberOfPins: 3
readonly property var acceptedImageExtensions: [".png", ".jpg", ".jpeg", ".svg", ".gif"]
readonly property var acceptedDragNDropImageExtensions: [".png", ".jpg", ".jpeg", ".heif", "tif", ".tiff"]
readonly property var acceptedDragNDropImageExtensions: [".png", ".jpg", ".jpeg", ".heif", ".tif", ".tiff"]
readonly property string mentionSpanTag: `<span style="background-color: ${Style.current.mentionBgColor};"><a style="color:${Style.current.mentionColor};text-decoration:none" href='http://'>`

View File

@ -38,7 +38,16 @@ Item {
signal openCreateChatView()
signal closeCreateChatView()
signal openProfilePopupRequested(string publicKey, var parentPopup, string state)
signal openProfilePopupRequested(string publicKey, var parentPopup)
signal openNicknamePopupRequested(string publicKey, string nickname, string subtitle)
signal nickNameChanged(string publicKey, string nickname)
signal blockContactRequested(string publicKey, string contactName)
signal contactBlocked(string publicKey)
signal unblockContactRequested(string publicKey, string contactName)
signal contactUnblocked(string publicKey)
signal openChangeProfilePicPopup()
signal displayToastMessage(string title, string subTitle, string icon, bool loading, int ephNotifType, string url)
signal openEditDisplayNamePopup()
@ -50,7 +59,7 @@ Item {
userPublicKey: publicKey,
userDisplayName: contactDetails.displayName,
userIcon: contactDetails.largeImage,
userIsEnsVerified: contactDetails.ensVerified,
userIsEnsVerified: contactDetails.ensVerified
})
}
@ -61,8 +70,56 @@ Item {
})
}
function openProfilePopup(publicKey, parentPopup, state = "") {
openProfilePopupRequested(publicKey, parentPopup, state);
function openSendIDRequestPopup(publicKey) {
const contactDetails = Utils.getContactDetailsAsJson(publicKey);
return openPopup(sendIDRequestPopupComponent, {
userPublicKey: publicKey,
userDisplayName: contactDetails.displayName,
userIcon: contactDetails.largeImage,
userIsEnsVerified: contactDetails.ensVerified,
"header.title": qsTr("Verify %1's Identity").arg(contactDetails.displayName),
challengeText: 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(contactDetails.displayName),
buttonText: qsTr("Send verification request")
})
}
function openIncomingIDRequestPopup(publicKey) {
try {
const request = appMain.rootStore.profileSectionStore.contactsStore.getVerificationDetailsFromAsJson(publicKey)
return openPopup(contactVerificationRequestPopupComponent, {
senderPublicKey: request.from,
senderDisplayName: request.displayName,
senderIcon: request.icon,
challengeText: request.challenge,
responseText: request.response,
messageTimestamp: request.requestedAt,
responseTimestamp: request.repliedAt
})
} catch (e) {
console.error("Error getting or parsing verification data", e)
}
}
function openOutgoingIDRequestPopup(publicKey) {
try {
const verificationDetails = appMain.rootStore.profileSectionStore.contactsStore.getSentVerificationDetailsAsJson(publicKey)
return openPopup(contactOutgoingVerificationRequestPopupComponent, {
userPublicKey: publicKey,
verificationStatus: verificationDetails.requestStatus,
verificationChallenge: verificationDetails.challenge,
verificationResponse: verificationDetails.response,
verificationResponseDisplayName: verificationDetails.displayName,
verificationResponseIcon: verificationDetails.icon,
verificationRequestedAt: verificationDetails.requestedAt,
verificationRepliedAt: verificationDetails.repliedAt
})
} catch (e) {
console.error("Error getting or parsing verification data", e)
}
}
function openProfilePopup(publicKey, parentPopup) {
openProfilePopupRequested(publicKey, parentPopup)
}
function openActivityCenterPopup() {
@ -145,9 +202,45 @@ Item {
anchors.centerIn: parent
rootStore: appMain.rootStore
contactsStore: appMain.rootStore.contactStore
onClosed: {
destroy()
onClosed: destroy()
}
}
Component {
id: sendIDRequestPopupComponent
SendContactRequestModal {
anchors.centerIn: parent
onAccepted: appMain.rootStore.profileSectionStore.contactsStore.sendVerificationRequest(userPublicKey, message)
onClosed: destroy()
}
}
Component {
id: contactVerificationRequestPopupComponent
ContactVerificationRequestPopup {
onResponseSent: {
appMain.rootStore.profileSectionStore.contactsStore.acceptVerificationRequest(senderPublicKey, response)
}
onVerificationRefused: {
appMain.rootStore.profileSectionStore.contactsStore.declineVerificationRequest(senderPublicKey)
}
onClosed: destroy()
}
}
Component {
id: contactOutgoingVerificationRequestPopupComponent
OutgoingContactVerificationRequestPopup {
onVerificationRequestCanceled: {
appMain.rootStore.profileSectionStore.contactsStore.cancelVerificationRequest(userPublicKey)
}
onUntrustworthyVerified: {
appMain.rootStore.profileSectionStore.contactsStore.verifiedUntrustworthy(userPublicKey)
}
onTrustedVerified: {
appMain.rootStore.profileSectionStore.contactsStore.verifiedTrusted(userPublicKey)
}
onClosed: destroy()
}
}
}