refactor ProfileContextMenu to make it a functional component

refactor ProfileContextMenu to make it a functional component

refactor ProfileContextMenu to make it a functional component

refactor ProfileContextMenu to make it a functional component

This refactor ProfileContextMenu to make it a functional component by:

refactored out direct calls to backend, and passing backend data structures and moved this logic to the callers, also refactored common calls between the callers
common types of context menus have been extracted to their sub components which removes a lot of logic too and makes the behaviour very clear
user verification workflow (which was already disabled) has been removed

refactor: use signals and call singletons on the parent instead

remove unused code for now from profile context menu

refactor profile context menu into two components; add property to storybook

extract blocked profile context menu and self profile context menu

use profileType instead of individual bools

refactor to pass trustStatus as an argument

make contact type a parameter

remove unnecessary method from RegularProfileContextMenu

add ensVerified property to ProfileContextMenu components

add onlineStatus property to ProfileContextMenu components

move ProfileContextMenu storybook controls to the right sidebar

move contactDetails logic up from the view

add local nickname property to ProfileContextMenu components

fix issue with missing signal; fix logs in storybook

use constant for profileType instead of string

refactor common code into a single method

refactor getProfileContext

remove references to contactDetails which are not longer needed

remove unnecessary comments

fix bridged constant

refactor into a single ProfileContextMenu component

refactor into a single ProfileContextMenu component

refactor into a single ProfileContextMenu component

simplify imports

remove unused store field

move methods from utils to contacts store

remove onClosed signal

remove unused param

rename ProfileContextMenu variables

simplify signals in ProfileContextMenu

remove ;

refactor: do early return

simplify ifs

move ProfileContextMenu to its own storybook page

fix wrong params

fix profile context menu separator

add missing signals to profile context menu on the members tab panel
This commit is contained in:
Iuri Matias 2024-09-06 11:55:44 -04:00
parent 1faa8e6561
commit 96d0760488
9 changed files with 594 additions and 307 deletions

View File

@ -11,6 +11,7 @@ import Models 1.0
import utils 1.0
import shared.views.chat 1.0
import shared.status 1.0
SplitView {
@ -30,30 +31,22 @@ SplitView {
color: Theme.palette.statusAppLayout.rightPanelBackgroundColor
clip: true
RowLayout {
ColumnLayout {
anchors.centerIn: parent
Button {
text: "Message context menu"
onClicked: {
menu1.createObject(this).popup()
spacing: 10
RowLayout {
Button {
text: "Message context menu"
onClicked: {
menu1.createObject(this).popup()
}
}
}
Button {
text: "Message context menu (hide disabled items)"
onClicked: {
menu2.createObject(this).popup()
}
}
Button {
text: "Profile context menu"
onClicked: {
menu3.createObject(this).popup()
}
}
Button {
text: "Profile context menu (hide disabled items)"
onClicked: {
menu4.createObject(this).popup()
Button {
text: "Message context menu (hide disabled items)"
onClicked: {
menu2.createObject(this).popup()
}
}
}
}
@ -79,53 +72,22 @@ SplitView {
}
}
}
Component {
id: menu3
ProfileContextMenu {
anchors.centerIn: parent
hideDisabledItems: false
onClosed: {
destroy()
}
}
}
Component {
id: menu4
ProfileContextMenu {
anchors.centerIn: parent
hideDisabledItems: true
onClosed: {
destroy()
}
}
}
}
LogsAndControlsPanel {
id: logsAndControlsPanel
SplitView.minimumHeight: 100
SplitView.preferredHeight: 150
logsView.logText: logs.logText
}
}
Pane {
SplitView.minimumWidth: 300
SplitView.preferredWidth: 300
LogsAndControlsPanel {
id: logsAndControlsPanel
ScrollView {
anchors.fill: parent
SplitView.minimumWidth: 150
SplitView.preferredWidth: 250
ColumnLayout {
spacing: 16
logsView.logText: logs.logText
}
controls: ColumnLayout {
spacing: 16
}
}
}
// category: Views

View File

@ -0,0 +1,280 @@
import QtQuick 2.14
import QtQuick.Controls 2.14
import QtQuick.Layouts 1.14
import StatusQ.Controls 0.1
import StatusQ.Core.Theme 0.1
import StatusQ.Components 0.1
import Storybook 1.0
import Models 1.0
import utils 1.0
import shared.views.chat 1.0
import shared.status 1.0
SplitView {
QtObject {
id: d
}
Logs { id: logs }
SplitView {
orientation: Qt.Vertical
SplitView.fillWidth: true
Rectangle {
SplitView.fillWidth: true
SplitView.fillHeight: true
color: Theme.palette.statusAppLayout.rightPanelBackgroundColor
clip: true
ColumnLayout {
anchors.centerIn: parent
spacing: 10
RowLayout {
Button {
text: "Profile context menu"
onClicked: {
menu1.createObject(this).popup()
}
}
Button {
text: "Profile context menu (hide disabled items)"
onClicked: {
menu2.createObject(this).popup()
}
}
}
}
Component {
id: menu1
ProfileContextMenu {
id: profileContextMenu
anchors.centerIn: parent
hideDisabledItems: false
profileType: profileTypeSelector.currentValue
trustStatus: trustStatusSelector.currentValue
contactType: contactTypeSelector.currentValue
ensVerified: ensVerifiedCheckBox.checked
onlineStatus: onlineStatusSelector.currentValue
hasLocalNickname: hasLocalNicknameCheckBox.checked
publicKey: publicKeyInput.text
onOpenProfileClicked: () => {
logs.logEvent("Open profile clicked for:", profileContextMenu.publicKey)
}
onCreateOneToOneChat: () => {
logs.logEvent("Create one-to-one chat:", profileContextMenu.publicKey)
}
onReviewContactRequest: () => {
logs.logEvent("Review contact request:", profileContextMenu.publicKey)
}
onSendContactRequest: () => {
logs.logEvent("Send contact request:", profileContextMenu.publicKey)
}
onEditNickname: () => {
logs.logEvent("Edit nickname:", profileContextMenu.publicKey)
}
onRemoveNickname: (displayName) => {
logs.logEvent("Remove nickname:", profileContextMenu.publicKey, displayName)
}
onUnblockContact: () => {
logs.logEvent("Unblock contact:", profileContextMenu.publicKey)
}
onMarkAsUntrusted: () => {
logs.logEvent("Mark as untrusted:", profileContextMenu.publicKey)
}
onRemoveTrustStatus: () => {
logs.logEvent("Remove trust status:", profileContextMenu.publicKey)
}
onRemoveContact: () => {
logs.logEvent("Remove contact:", profileContextMenu.publicKey)
}
onBlockContact: () => {
logs.logEvent("Block contact:", profileContextMenu.publicKey)
}
onClosed: {
destroy()
}
}
}
Component {
id: menu2
ProfileContextMenu {
id: profileContextMenu
anchors.centerIn: parent
hideDisabledItems: true
profileType: profileTypeSelector.currentValue
trustStatus: trustStatusSelector.currentValue
contactType: contactTypeSelector.currentValue
ensVerified: ensVerifiedCheckBox.checked
onlineStatus: onlineStatusSelector.currentValue
hasLocalNickname: hasLocalNicknameCheckBox.checked
publicKey: publicKeyInput.text
onOpenProfileClicked: () => {
logs.logEvent("Open profile clicked for:", profileContextMenu.publicKey)
}
onCreateOneToOneChat: () => {
logs.logEvent("Create one-to-one chat:", profileContextMenu.publicKey)
}
onReviewContactRequest: () => {
logs.logEvent("Review contact request:", profileContextMenu.publicKey)
}
onSendContactRequest: () => {
logs.logEvent("Send contact request:", profileContextMenu.publicKey)
}
onEditNickname: () => {
logs.logEvent("Edit nickname:", profileContextMenu.publicKey)
}
onRemoveNickname: (displayName) => {
logs.logEvent("Remove nickname:", profileContextMenu.publicKey, displayName)
}
onUnblockContact: () => {
logs.logEvent("Unblock contact:", profileContextMenu.publicKey)
}
onMarkAsUntrusted: () => {
logs.logEvent("Mark as untrusted:", profileContextMenu.publicKey)
}
onRemoveTrustStatus: () => {
logs.logEvent("Remove trust status:", profileContextMenu.publicKey)
}
onRemoveContact: () => {
logs.logEvent("Remove contact:", profileContextMenu.publicKey)
}
onBlockContact: () => {
logs.logEvent("Block contact:", profileContextMenu.publicKey)
}
onClosed: {
destroy()
}
}
}
}
}
LogsAndControlsPanel {
id: logsAndControlsPanel
SplitView.minimumWidth: 150
SplitView.preferredWidth: 250
logsView.logText: logs.logText
controls: ColumnLayout {
spacing: 16
TextField {
id: publicKeyInput
Layout.fillWidth: true
placeholderText: "Enter public key"
}
Label {
Layout.fillWidth: true
text: "Public Key: " + (publicKeyInput.text || "0x047d6710733523714e65e783f975f2c02f5a0f43ecf6febb4e0fadb48bffae32cdc749ea366b2649c271b76e568e9bf0c173596c6e54a2659293a46947b33c9d72")
elide: Text.ElideMiddle
}
ComboBox {
id: profileTypeSelector
textRole: "text"
valueRole: "value"
model: [
{ text: "Regular", value: Constants.profileType.regular },
{ text: "Self", value: Constants.profileType.self },
{ text: "Blocked", value: Constants.profileType.blocked },
{ text: "Bridged", value: Constants.profileType.bridged }
]
currentIndex: 0
}
ComboBox {
id: trustStatusSelector
textRole: "text"
valueRole: "value"
model: [
{ text: "Unknown", value: Constants.trustStatus.unknown },
{ text: "Trusted", value: Constants.trustStatus.trusted },
{ text: "Untrusted", value: Constants.trustStatus.untrustworthy }
]
currentIndex: 0
}
ComboBox {
id: contactTypeSelector
textRole: "text"
valueRole: "value"
model: [
{ text: "Non Contact", value: Constants.contactType.nonContact },
{ text: "Contact", value: Constants.contactType.contact },
{ text: "Contact Request Received", value: Constants.contactType.contactRequestReceived },
{ text: "Contact Request Sent", value: Constants.contactType.contactRequestSent }
]
currentIndex: 0
}
CheckBox {
id: ensVerifiedCheckBox
text: "ENS Verified"
checked: false
}
Label {
Layout.fillWidth: true
text: "ENS Verified: " + (ensVerifiedCheckBox.checked ? "Yes" : "No")
}
Label {
Layout.fillWidth: true
text: "Profile type: " + profileTypeSelector.currentText
}
Label {
Layout.fillWidth: true
text: "Trust status: " + trustStatusSelector.currentText
}
Label {
Layout.fillWidth: true
text: "Contact type: " + contactTypeSelector.currentText
}
ComboBox {
id: onlineStatusSelector
textRole: "text"
valueRole: "value"
model: [
{ text: "Unknown", value: Constants.onlineStatus.unknown },
{ text: "Inactive", value: Constants.onlineStatus.inactive },
{ text: "Online", value: Constants.onlineStatus.online }
]
currentIndex: 2 // Default to online
}
Label {
Layout.fillWidth: true
text: "Online status: " + onlineStatusSelector.currentText
}
CheckBox {
id: hasLocalNicknameCheckBox
text: "Has Local Nickname"
checked: false
}
Label {
Layout.fillWidth: true
text: "Has Local Nickname: " + (hasLocalNicknameCheckBox.checked ? "Yes" : "No")
}
}
}
}
// category: Views

View File

@ -127,11 +127,13 @@ Item {
ringSettings.ringSpecModel: model.colorHash
onClicked: {
if (mouse.button === Qt.RightButton) {
const { profileType, trustStatus, contactType, ensVerified, onlineStatus, hasLocalNickname } = root.store.contactsStore.getProfileContext(model.pubKey, userProfile.pubKey)
Global.openMenu(profileContextMenuComponent, this, {
myPublicKey: root.store.myPublicKey(),
selectedUserPublicKey: model.pubKey,
selectedUserDisplayName: nickName || userName,
selectedUserIcon: model.icon,
profileType, trustStatus, contactType, ensVerified, onlineStatus, hasLocalNickname,
publicKey: model.pubKey,
displayName: nickName || userName,
userIcon: model.icon,
})
} else if (mouse.button === Qt.LeftButton) {
Global.openProfilePopup(model.pubKey)
@ -168,17 +170,44 @@ Item {
id: profileContextMenuComponent
ProfileContextMenu {
store: root.store
id: profileContextMenu
margins: 8
onOpenProfileClicked: {
Global.openProfilePopup(publicKey, null)
}
onOpenProfileClicked: Global.openProfilePopup(profileContextMenu.publicKey, null)
onCreateOneToOneChat: {
Global.changeAppSectionBySectionType(Constants.appSection.chat)
root.store.chatCommunitySectionModule.createOneToOneChat(communityId, chatId, ensName)
root.store.chatCommunitySectionModule.createOneToOneChat("", profileContextMenu.publicKey, "")
}
onClosed: {
destroy()
onReviewContactRequest: {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.openReviewContactRequestPopup(profileContextMenu.publicKey, contactDetails, null)
}
onSendContactRequest: {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.openContactRequestPopup(profileContextMenu.publicKey, contactDetails, null)
}
onEditNickname: {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.openNicknamePopupRequested(profileContextMenu.publicKey, contactDetails, null)
}
onRemoveNickname: (displayName) => {
root.store.contactsStore.changeContactNickname(profileContextMenu.publicKey, "", displayName, true)
}
onUnblockContact: {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.unblockContactRequested(profileContextMenu.publicKey, contactDetails)
}
onMarkAsUntrusted: {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.markAsUntrustedRequested(profileContextMenu.publicKey, contactDetails)
}
onRemoveTrustStatus: root.store.contactsStore.removeTrustStatus(profileContextMenu.publicKey)
onRemoveContact: {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.removeContactRequested(profileContextMenu.publicKey, contactDetails)
}
onBlockContact: {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.blockContactRequested(profileContextMenu.publicKey, contactDetails)
}
}
}

View File

@ -305,11 +305,15 @@ Item {
onClicked: {
if(mouse.button === Qt.RightButton) {
const { profileType, trustStatus, contactType, ensVerified, onlineStatus, hasLocalNickname } = root.rootStore.contactsStore.getProfileContext(model.pubKey, Global.userProfile.pubKey)
Global.openMenu(memberContextMenuComponent, this, {
selectedUserPublicKey: model.pubKey,
selectedUserDisplayName: memberItem.title,
selectedUserIcon: icon.name,
})
profileType, trustStatus, contactType, ensVerified, onlineStatus, hasLocalNickname,
myPublicKey: Global.userProfile.pubKey,
publicKey: model.pubKey,
displayName: memberItem.title,
userIcon: icon.name,
})
} else {
Global.openProfilePopup(model.pubKey)
}
@ -323,18 +327,43 @@ Item {
ProfileContextMenu {
id: memberContextMenuView
store: root.rootStore
myPublicKey: Global.userProfile.pubKey
onOpenProfileClicked: {
Global.openProfilePopup(publicKey, null)
}
onOpenProfileClicked: Global.openProfilePopup(memberContextMenuView.publicKey, null)
onCreateOneToOneChat: {
Global.changeAppSectionBySectionType(Constants.appSection.chat)
root.rootStore.chatCommunitySectionModule.createOneToOneChat(communityId, chatId, ensName)
root.rootStore.chatCommunitySectionModule.createOneToOneChat("", membersContextMenuView.publicKey, "")
}
onClosed: {
destroy()
onReviewContactRequest: {
const contactDetails = memberContextMenuView.publicKey === "" ? {} : Utils.getContactDetailsAsJson(memberContextMenuView.publicKey, true, true)
Global.openReviewContactRequestPopup(memberContextMenuView.publicKey, contactDetails, null)
}
onSendContactRequest: {
const contactDetails = memberContextMenuView.publicKey === "" ? {} : Utils.getContactDetailsAsJson(memberContextMenuView.publicKey, true, true)
Global.openContactRequestPopup(memberContextMenuView.publicKey, contactDetails, null)
}
onEditNickname: {
const contactDetails = memberContextMenuView.publicKey === "" ? {} : Utils.getContactDetailsAsJson(memberContextMenuView.publicKey, true, true)
Global.openNicknamePopupRequested(memberContextMenuView.publicKey, contactDetails, null)
}
onRemoveNickname: (displayName) => {
root.store.contactsStore.changeContactNickname(memberContextMenuView.publicKey, "", displayName, true)
}
onUnblockContact: {
const contactDetails = memberContextMenuView.publicKey === "" ? {} : Utils.getContactDetailsAsJson(memberContextMenuView.publicKey, true, true)
Global.unblockContactRequested(memberContextMenuView.publicKey, contactDetails)
}
onMarkAsUntrusted: {
const contactDetails = memberContextMenuView.publicKey === "" ? {} : Utils.getContactDetailsAsJson(memberContextMenuView.publicKey, true, true)
Global.markAsUntrustedRequested(memberContextMenuView.publicKey, contactDetails)
}
onRemoveTrustStatus: root.store.contactsStore.removeTrustStatus(memberContextMenuView.publicKey)
onRemoveContact: {
const contactDetails = memberContextMenuView.publicKey === "" ? {} : Utils.getContactDetailsAsJson(memberContextMenuView.publicKey, true, true)
Global.removeContactRequested(memberContextMenuView.publicKey, contactDetails)
}
onBlockContact: {
const contactDetails = memberContextMenuView.publicKey === "" ? {} : Utils.getContactDetailsAsJson(memberContextMenuView.publicKey, true, true)
Global.blockContactRequested(memberContextMenuView.publicKey, contactDetails)
}
}
}

View File

@ -178,4 +178,44 @@ QtObject {
function getLinkToProfile(publicKey) {
return root.contactsModule.shareUserUrlWithData(publicKey)
}
function getProfileContext(publicKey, myPublicKey, isBridgedAccount = false) {
const contactDetails = Utils.getContactDetailsAsJson(publicKey, true, true)
if (!contactDetails)
return {
profileType: getProfileType(publicKey, myPublicKey, isBridgedAccount, false),
trustStatus: Constants.trustStatus.unknown,
contactType: getContactType(Constants.ContactRequestState.None, false),
ensVerified: false,
onlineStatus: Constants.onlineStatus.unknown,
hasLocalNickname: false
}
const isBlocked = contactDetails.isBlocked
const profileType = getProfileType(publicKey, myPublicKey, isBridgedAccount, isBlocked)
const contactType = getContactType(contactDetails.contactRequestState, contactDetails.isContact)
const trustStatus = contactDetails.trustStatus
const ensVerified = contactDetails.ensVerified
const onlineStatus = contactDetails.onlineStatus
const hasLocalNickname = !!contactDetails.localNickname
return { profileType, trustStatus, contactType, ensVerified, onlineStatus, hasLocalNickname }
}
function getProfileType(publicKey, myPublicKey, isBridgedAccount, isBlocked) {
if (publicKey === myPublicKey) return Constants.profileType.self
if (isBridgedAccount) return Constants.profileType.bridged
if (isBlocked) return Constants.profileType.blocked
return Constants.profileType.regular
}
function getContactType(contactRequestState, isContact) {
switch (contactRequestState) {
case Constants.ContactRequestState.Received:
return Constants.contactType.contactRequestReceived
case Constants.ContactRequestState.Sent:
return Constants.contactType.contactRequestSent
default:
return isContact ? Constants.contactType.contact : Constants.contactType.nonContact
}
}
}

View File

@ -34,11 +34,14 @@ SettingsContentBase {
}
function openContextMenu(publicKey, name, icon) {
const { profileType, trustStatus, contactType, ensVerified, onlineStatus, hasLocalNickname } = root.contactsStore.getProfileContext(publicKey, root.contactsStore.myPublicKey)
Global.openMenu(contactContextMenuComponent, this, {
selectedUserPublicKey: publicKey,
selectedUserDisplayName: name,
selectedUserIcon: icon,
})
profileType, trustStatus, contactType, ensVerified, onlineStatus, hasLocalNickname,
publicKey: publicKey,
displayName: name,
userIcon: icon,
})
}
Item {
@ -49,23 +52,45 @@ SettingsContentBase {
Component {
id: contactContextMenuComponent
ProfileContextMenu {
id: contactContextMenu
store: ({contactsStore: root.contactsStore})
onOpenProfileClicked: function (pubkey) {
Global.openProfilePopup(pubkey, null, null)
onOpenProfileClicked: Global.openProfilePopup(contactContextMenu.publicKey, null, null)
onCreateOneToOneChat: root.contactsStore.joinPrivateChat(contactContextMenu.publicKey)
onReviewContactRequest: () => {
const contactDetails = contactContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(contactContextMenu.publicKey, true, true)
Global.openReviewContactRequestPopup(contactContextMenu.publicKey, contactDetails, null)
}
onCreateOneToOneChat: function (communityId, chatId, ensName) {
root.contactsStore.joinPrivateChat(chatId)
onSendContactRequest: () => {
const contactDetails = contactContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(contactContextMenu.publicKey, true, true)
Global.openContactRequestPopup(contactContextMenu.publicKey, contactDetails, null)
}
onClosed: {
destroy()
onEditNickname: () => {
const contactDetails = contactContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(contactContextMenu.publicKey, true, true)
Global.openNicknamePopupRequested(contactContextMenu.publicKey, contactDetails, null)
}
onRemoveNickname: (displayName) => {
root.contactsStore.changeContactNickname(contactContextMenu.publicKey, "", displayName, true)
}
onUnblockContact: () => {
const contactDetails = contactContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(contactContextMenu.publicKey, true, true)
Global.unblockContactRequested(contactContextMenu.publicKey, contactDetails)
}
onMarkAsUntrusted: () => {
const contactDetails = contactContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(contactContextMenu.publicKey, true, true)
Global.markAsUntrustedRequested(contactContextMenu.publicKey, contactDetails)
}
onRemoveTrustStatus: root.contactsStore.removeTrustStatus(contactContextMenu.publicKey)
onRemoveContact: () => {
const contactDetails = contactContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(contactContextMenu.publicKey, true, true)
Global.removeContactRequested(contactContextMenu.publicKey, contactDetails)
}
onBlockContact: () => {
const contactDetails = contactContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(contactContextMenu.publicKey, true, true)
Global.blockContactRequested(contactContextMenu.publicKey, contactDetails)
}
}
}
SearchBox {
id: searchBox
anchors.left: parent.left
@ -173,7 +198,7 @@ SettingsContentBase {
visible: root.contactsStore.myContactsModel.count === 0
NoFriendsRectangle {
anchors.centerIn: parent
text: qsTr("You dont have any contacts yet")
text: qsTr("You don't have any contacts yet")
}
}
}

View File

@ -154,12 +154,14 @@ Loader {
// so we don't enable to right click the unavailable profile
return false
}
const publicKey = isReply ? quotedMessageFrom : root.senderId
const isBridgedAccount = isReply ? (quotedMessageContentType === Constants.messageContentType.bridgeMessageType) : root.isBridgeMessage
const { profileType, trustStatus, contactType, ensVerified, onlineStatus, hasLocalNickname } = root.contactsStore.getProfileContext(publicKey, root.rootStore.contactsStore.myPublicKey, isBridgedAccount)
const params = {
selectedUserPublicKey: isReply ? quotedMessageFrom : root.senderId,
selectedUserDisplayName: isReply ? quotedMessageAuthorDetailsDisplayName : root.senderDisplayName,
selectedUserIcon: isReply ? quotedMessageAuthorDetailsThumbnailImage : root.senderIcon,
isBridgedAccount: isReply ? (quotedMessageContentType === Constants.messageContentType.bridgeMessageType) : root.isBridgeMessage
const params = { profileType, trustStatus, contactType, ensVerified, onlineStatus, hasLocalNickname,
publicKey: isReply ? quotedMessageFrom : root.senderId,
displayName: isReply ? quotedMessageAuthorDetailsDisplayName : root.senderDisplayName,
userIcon: isReply ? quotedMessageAuthorDetailsThumbnailImage : root.senderIcon,
}
Global.openMenu(profileContextMenuComponent, sender, params)
@ -1178,23 +1180,46 @@ Loader {
Component {
id: profileContextMenuComponent
ProfileContextMenu {
store: root.rootStore
onOpenProfileClicked: (publicKey) => {
Global.openProfilePopup(publicKey, null)
}
onCreateOneToOneChat: (communityId, chatId, ensName) => {
id: profileContextMenu
onOpenProfileClicked: Global.openProfilePopup(profileContextMenu.publicKey, null)
onCreateOneToOneChat: () => {
Global.changeAppSectionBySectionType(Constants.appSection.chat)
root.rootStore.chatCommunitySectionModule.createOneToOneChat("", chatId, ensName)
root.rootStore.chatCommunitySectionModule.createOneToOneChat("", profileContextMenu.publicKey, "")
}
onOpened: {
root.setMessageActive(root.messageId, true)
onReviewContactRequest: () => {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.openReviewContactRequestPopup(profileContextMenu.publicKey, contactDetails, null)
}
onClosed: {
root.setMessageActive(root.messageId, false)
destroy()
onSendContactRequest: () => {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.openContactRequestPopup(profileContextMenu.publicKey, contactDetails, null)
}
onEditNickname: () => {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.openNicknamePopupRequested(profileContextMenu.publicKey, contactDetails, null)
}
onRemoveNickname: () => {
root.rootStore.contactsStore.changeContactNickname(profileContextMenu.publicKey, "", profileContextMenu.displayName, true)
}
onUnblockContact: () => {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.unblockContactRequested(profileContextMenu.publicKey, contactDetails)
}
onMarkAsUntrusted: () => {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.markAsUntrustedRequested(profileContextMenu.publicKey, contactDetails)
}
onRemoveTrustStatus: root.rootStore.contactsStore.removeTrustStatus(profileContextMenu.publicKey)
onRemoveContact: () => {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.removeContactRequested(profileContextMenu.publicKey, contactDetails)
}
onBlockContact: () => {
const contactDetails = profileContextMenu.publicKey === "" ? {} : Utils.getContactDetailsAsJson(profileContextMenu.publicKey, true, true)
Global.blockContactRequested(profileContextMenu.publicKey, contactDetails)
}
onOpened: root.setMessageActive(root.messageId, true)
}
}

View File

@ -1,99 +1,38 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
import StatusQ.Popups 0.1
import StatusQ.Components 0.1
import StatusQ.Core.Utils 0.1 as StatusQUtils
import AppLayouts.Chat.stores 1.0 as ChatStores
import utils 1.0
import shared 1.0
import shared.panels 1.0
import shared.popups 1.0
import shared.status 1.0
import shared.controls.chat 1.0
import shared.controls.chat.menuItems 1.0
StatusMenu {
id: root
property ChatStores.RootStore store
property string publicKey: ""
property string displayName: ""
property string userIcon: ""
property int trustStatus: Constants.trustStatus.unknown
property int contactType: Constants.contactType.nonContact
property int onlineStatus: Constants.onlineStatus.unknown
property int profileType: Constants.profileType.regular
property bool ensVerified: false
property bool hasLocalNickname: false
property string myPublicKey: ""
property string selectedUserPublicKey: ""
property string selectedUserDisplayName: ""
property string selectedUserIcon: ""
property bool isBridgedAccount: false
readonly property bool isMe: {
return root.selectedUserPublicKey === root.store.contactsStore.myPublicKey;
}
readonly property var contactDetails: {
if (root.selectedUserPublicKey === "" || isMe) {
return {}
}
return Utils.getContactDetailsAsJson(root.selectedUserPublicKey, true, true);
}
readonly property bool isContact: {
return root.selectedUserPublicKey !== "" && !!contactDetails.isContact
}
readonly property bool isBlockedContact: (!!contactDetails && contactDetails.isBlocked) || false
readonly property bool idVerificationFlowsEnabled: false // disabled temporarily as per https://github.com/status-im/status-desktop/issues/14954
readonly property int outgoingVerificationStatus: {
if (root.selectedUserPublicKey === "" || root.isMe || !root.isContact) {
return 0
}
return contactDetails.verificationStatus
}
readonly property int incomingVerificationStatus: {
if (root.selectedUserPublicKey === "" || root.isMe || !root.isContact) {
return 0
}
return contactDetails.incomingVerificationStatus
}
readonly property bool hasPendingContactRequest: {
return !root.isMe && root.selectedUserPublicKey !== "" &&
contactDetails.contactRequestState === Constants.ContactRequestState.Received
}
readonly property bool hasActiveReceivedVerificationRequestFrom: {
if (!root.selectedUserPublicKey || root.isMe || !root.isContact) {
return false
}
return contactDetails.incomingVerificationStatus === Constants.verificationStatus.verifying ||
contactDetails.incomingVerificationStatus === Constants.verificationStatus.verified
}
readonly property bool isVerificationRequestSent: {
if (!root.selectedUserPublicKey || root.isMe || !root.isContact) {
return false
}
return root.outgoingVerificationStatus !== Constants.verificationStatus.unverified &&
root.outgoingVerificationStatus !== Constants.verificationStatus.verified &&
root.outgoingVerificationStatus !== Constants.verificationStatus.trusted
}
readonly property bool isTrusted: {
if (!root.selectedUserPublicKey || root.isMe || !root.isContact) {
return false
}
return root.outgoingVerificationStatus === Constants.verificationStatus.trusted ||
root.incomingVerificationStatus === Constants.verificationStatus.trusted
}
readonly property bool userTrustIsUnknown: contactDetails && contactDetails.trustStatus === Constants.trustStatus.unknown
readonly property bool userIsUntrustworthy: contactDetails && contactDetails.trustStatus === Constants.trustStatus.untrustworthy
readonly property bool userIsLocallyTrusted: contactDetails && contactDetails.trustStatus === Constants.trustStatus.trusted
signal openProfileClicked(string publicKey)
signal createOneToOneChat(string communityId, string chatId, string ensName)
onClosed: {
// Reset selectedUserPublicKey so that associated properties get recalculated on re-open
selectedUserPublicKey = ""
}
signal openProfileClicked
signal createOneToOneChat
signal reviewContactRequest
signal sendContactRequest
signal editNickname
signal removeNickname(string displayName)
signal unblockContact
signal markAsUntrusted
signal removeTrustStatus
signal removeContact
signal blockContact
ProfileHeader {
width: parent.width
@ -102,196 +41,140 @@ StatusMenu {
displayNameVisible: false
displayNamePlusIconsVisible: true
editButtonVisible: false
displayName: StatusQUtils.Emoji.parse(root.selectedUserDisplayName, StatusQUtils.Emoji.size.verySmall)
pubkey: root.selectedUserPublicKey
icon: root.selectedUserIcon
trustStatus: contactDetails && contactDetails.trustStatus ? contactDetails.trustStatus
: Constants.trustStatus.unknown
displayName: StatusQUtils.Emoji.parse(root.displayName, StatusQUtils.Emoji.size.verySmall)
pubkey: root.publicKey
icon: root.userIcon
trustStatus: root.profileType === Constants.profileType.regular ? root.trustStatus : Constants.trustStatus.unknown
isContact: root.profileType === Constants.profileType.regular ? root.contactType === Constants.contactType.contact : false
isBlocked: root.profileType === Constants.profileType.blocked
isCurrentUser: root.profileType === Constants.profileType.self
userIsEnsVerified: root.ensVerified
isBridgedAccount: root.profileType === Constants.profileType.bridged
Binding on onlineStatus {
value: contactDetails.onlineStatus
when: !root.isMe
value: root.onlineStatus
when: root.profileType !== Constants.profileType.bridged
}
isContact: root.isContact
isBlocked: root.isBlockedContact
isCurrentUser: root.isMe
userIsEnsVerified: (!!contactDetails && contactDetails.ensVerified) || false
isBridgedAccount: root.isBridgedAccount
}
StatusMenuSeparator {
topPadding: root.topPadding
visible: !root.isBridgedAccount
}
ViewProfileMenuItem {
id: viewProfileAction
objectName: "viewProfile_StatusItem"
enabled: !root.isBridgedAccount
enabled: root.profileType !== Constants.profileType.bridged
onTriggered: {
root.openProfileClicked(root.selectedUserPublicKey)
root.openProfileClicked()
root.close()
}
}
// Edit Nickname
StatusAction {
id: renameAction
objectName: "rename_StatusItem"
enabled: root.profileType === Constants.profileType.blocked || root.profileType === Constants.profileType.regular
text: root.hasLocalNickname ? qsTr("Edit nickname") : qsTr("Add nickname")
icon.name: "edit_pencil"
onTriggered: root.editNickname()
}
// Review Contact Request
StatusAction {
text: qsTr("Review contact request")
objectName: "reviewContactRequest_StatusItem"
icon.name: "add-contact"
enabled: !root.isMe && !root.isContact && !root.isBridgedAccount && !root.isBlockedContact && root.hasPendingContactRequest
onTriggered: Global.openReviewContactRequestPopup(root.selectedUserPublicKey, root.contactDetails, null)
enabled: root.profileType === Constants.profileType.regular && root.contactType === Constants.contactType.contactRequestReceived
onTriggered: root.reviewContactRequest()
}
// Send Message
SendMessageMenuItem {
id: sendMessageMenuItem
objectName: "sendMessage_StatusItem"
enabled: root.isContact && !root.isBlockedContact && !root.isBridgedAccount
enabled: root.profileType === Constants.profileType.regular && root.contactType === Constants.contactType.contact
onTriggered: {
root.createOneToOneChat("", root.selectedUserPublicKey, "")
root.createOneToOneChat()
root.close()
}
}
// Send Contact Request
SendContactRequestMenuItem {
id: sendContactRequestMenuItem
objectName: "sendContactRequest_StatusItem"
enabled: !root.isMe && !root.isContact && !root.isBlockedContact
&& (contactDetails.contactRequestState === Constants.ContactRequestState.None || contactDetails.contactRequestState === Constants.ContactRequestState.Dismissed)
&& !root.isBridgedAccount
onTriggered: Global.openContactRequestPopup(root.selectedUserPublicKey, root.contactDetails, null)
}
StatusAction {
id: verifyIdentityAction
text: qsTr("Request ID verification")
objectName: "verifyIdentity_StatusItem"
icon.name: "checkmark-circle"
enabled: idVerificationFlowsEnabled
&& !root.isMe && root.isContact
&& !root.isBlockedContact
&& !root.userIsLocallyTrusted
&& root.outgoingVerificationStatus === Constants.verificationStatus.unverified
&& !root.hasActiveReceivedVerificationRequestFrom
&& !root.isBridgedAccount
onTriggered: Global.openSendIDRequestPopup(root.selectedUserPublicKey, root.contactDetails, null)
}
StatusAction {
text: qsTr("Mark as ID verified")
objectName: "markAsVerified_StatusItem"
icon.name: "checkmark-circle"
enabled: idVerificationFlowsEnabled && !root.isMe && root.isContact && !root.isBridgedAccount && !root.isBlockedContact && !(root.isTrusted || root.userIsLocallyTrusted)
onTriggered: Global.openMarkAsIDVerifiedPopup(root.selectedUserPublicKey, root.contactDetails, null)
}
StatusAction {
id: pendingIdentityAction
objectName: "pendingIdentity_StatusItem"
text: {
if (root.isVerificationRequestSent) {
if (root.incomingVerificationStatus !== Constants.verificationStatus.verified)
return qsTr("ID verification pending...")
return qsTr("Review ID verification reply")
}
return qsTr("Reply to ID verification request")
}
icon.name: root.isVerificationRequestSent && root.incomingVerificationStatus !== Constants.verificationStatus.verified ? "history"
: "checkmark-circle"
enabled: idVerificationFlowsEnabled && !root.isMe && root.isContact && !root.isBridgedAccount && !root.isBlockedContact && !(root.isTrusted || root.userIsLocallyTrusted) &&
(root.hasActiveReceivedVerificationRequestFrom || root.isVerificationRequestSent)
onTriggered: {
if (root.hasActiveReceivedVerificationRequestFrom) {
Global.openIncomingIDRequestPopup(root.selectedUserPublicKey, root.contactDetails, null)
} else if (root.isVerificationRequestSent) {
Global.openOutgoingIDRequestPopup(root.selectedUserPublicKey, root.contactDetails, null)
}
root.close()
}
}
StatusAction {
id: renameAction
objectName: "rename_StatusItem"
text: contactDetails.localNickname ? qsTr("Edit nickname") : qsTr("Add nickname")
icon.name: "edit_pencil"
enabled: !root.isMe && !root.isBridgedAccount
onTriggered: Global.openNicknamePopupRequested(root.selectedUserPublicKey, root.contactDetails, null)
enabled: root.profileType === Constants.profileType.regular && root.contactType === Constants.contactType.nonContact
onTriggered: root.sendContactRequest()
}
StatusMenuSeparator {
visible: blockMenuItem.enabled || unblockAction.enabled
topPadding: root.topPadding
enabled: removeNicknameAction.enabled || unblockAction.enabled || markUntrustworthyMenuItem.enabled || removeUntrustworthyMarkMenuItem.enabled || removeContactAction.enabled || blockMenuItem.enabled
}
// Remove Nickname
StatusAction {
id: removeNicknameAction
text: qsTr("Remove nickname")
icon.name: "delete"
type: StatusAction.Type.Danger
enabled: !root.isMe && !!contactDetails.localNickname
onTriggered: root.store.contactsStore.changeContactNickname(root.selectedUserPublicKey, "", root.selectedUserDisplayName, true)
enabled: (root.profileType === Constants.profileType.blocked || root.profileType === Constants.profileType.regular) && root.hasLocalNickname
onTriggered: root.removeNickname(root.displayName)
}
// Unblock User
StatusAction {
id: unblockAction
objectName: "unblock_StatusItem"
enabled: root.profileType === Constants.profileType.blocked
text: qsTr("Unblock user")
icon.name: "cancel"
type: StatusAction.Type.Danger
enabled: !root.isMe && root.isBlockedContact && !root.isBridgedAccount
onTriggered: Global.unblockContactRequested(root.selectedUserPublicKey, root.contactDetails)
}
StatusAction {
objectName: "removeIDVerification_StatusItem"
text: qsTr("Remove ID verification")
icon.name: "delete"
type: StatusAction.Type.Danger
enabled: idVerificationFlowsEnabled && !root.isMe && root.isContact && !root.isBridgedAccount && (root.isTrusted || root.userIsLocallyTrusted)
onTriggered: Global.openRemoveIDVerificationDialog(root.selectedUserPublicKey, root.contactDetails, null)
onTriggered: root.unblockContact()
}
// Mark as Untrusted
StatusAction {
id: markUntrustworthyMenuItem
objectName: "markUntrustworthy_StatusItem"
text: qsTr("Mark as untrusted")
icon.name: "warning"
type: StatusAction.Type.Danger
enabled: !root.isMe && !root.userIsUntrustworthy && !root.isBridgedAccount && !root.isBlockedContact
onTriggered: Global.markAsUntrustedRequested(root.selectedUserPublicKey, root.contactDetails)
}
StatusAction {
text: qsTr("Cancel ID verification request")
icon.name: "delete"
type: StatusAction.Type.Danger
enabled: idVerificationFlowsEnabled && !root.isMe && root.isContact && !root.isBlockedContact && !root.isBridgedAccount && root.isVerificationRequestSent
onTriggered: root.store.contactsStore.cancelVerificationRequest(root.selectedUserPublicKey)
enabled: root.profileType === Constants.profileType.regular && root.trustStatus !== Constants.trustStatus.untrustworthy
onTriggered: root.markAsUntrusted()
}
// Remove Untrustworthy Mark
StatusAction {
id: removeUntrustworthyMarkMenuItem
objectName: "removeUntrustworthy_StatusItem"
text: qsTr("Remove untrusted mark")
icon.name: "warning"
type: StatusAction.Type.Danger
enabled: !root.isMe && root.userIsUntrustworthy && !root.isBridgedAccount
onTriggered: root.store.contactsStore.removeTrustStatus(root.selectedUserPublicKey)
enabled: root.profileType === Constants.profileType.regular && root.trustStatus === Constants.trustStatus.untrustworthy
onTriggered: root.removeTrustStatus()
}
// Remove Contact
StatusAction {
id: removeContactAction
text: qsTr("Remove contact")
objectName: "removeContact_StatusItem"
icon.name: "remove-contact"
type: StatusAction.Type.Danger
enabled: root.isContact && !root.isBlockedContact && !root.hasPendingContactRequest && !root.isBridgedAccount
onTriggered: Global.removeContactRequested(root.selectedUserPublicKey, root.contactDetails)
enabled: root.profileType === Constants.profileType.regular && root.contactType === Constants.contactType.contact
onTriggered: root.removeContact()
}
// Block User
StatusAction {
id: blockMenuItem
objectName: "blockUser_StatusItem"
text: qsTr("Block user")
icon.name: "cancel"
type: StatusAction.Type.Danger
enabled: !root.isMe && !root.isBlockedContact && !root.isBridgedAccount
onTriggered: Global.blockContactRequested(root.selectedUserPublicKey, root.contactDetails)
enabled: root.profileType === Constants.profileType.regular
onTriggered: root.blockContact()
}
}

View File

@ -430,6 +430,13 @@ QtObject {
readonly property int communityChat: 6
}
readonly property QtObject profileType: QtObject {
readonly property int regular: 0
readonly property int self: 1
readonly property int blocked: 2
readonly property int bridged: 3
}
readonly property QtObject memberRole: QtObject{
readonly property int none: 0
readonly property int owner: 1
@ -475,6 +482,13 @@ QtObject {
readonly property int responseToMessageWithId: 262 // ModelRole.ResponseToMessageWithId
}
readonly property QtObject contactType: QtObject {
readonly property int nonContact: 0
readonly property int contact: 1
readonly property int contactRequestReceived: 2
readonly property int contactRequestSent: 3
}
readonly property QtObject trustStatus: QtObject {
readonly property int unknown: 0
readonly property int trusted: 1