fix(MessageContextMenu): Cleanup. Separate menu for profile. (#10729)
This commit is contained in:
parent
f7e75208a5
commit
5ff4b5a435
|
@ -67,7 +67,9 @@ add_executable(
|
||||||
|
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||||
QML_IMPORT_ROOT="${CMAKE_CURRENT_LIST_DIR}"
|
QML_IMPORT_ROOT="${CMAKE_CURRENT_LIST_DIR}"
|
||||||
STATUSQ_MODULE_IMPORT_PATH="${STATUSQ_MODULE_IMPORT_PATH}")
|
STATUSQ_MODULE_IMPORT_PATH="${STATUSQ_MODULE_IMPORT_PATH}"
|
||||||
|
$<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(
|
target_link_libraries(
|
||||||
${PROJECT_LIB} PUBLIC Qt5::Core Qt5::Gui Qt5::Quick Qt5::QuickControls2)
|
${PROJECT_LIB} PUBLIC Qt5::Core Qt5::Gui Qt5::Quick Qt5::QuickControls2)
|
||||||
|
|
|
@ -61,6 +61,10 @@ ListModel {
|
||||||
title: "TokenListView"
|
title: "TokenListView"
|
||||||
section: "Views"
|
section: "Views"
|
||||||
}
|
}
|
||||||
|
ListElement {
|
||||||
|
title: "MessageContextMenu"
|
||||||
|
section: "Views"
|
||||||
|
}
|
||||||
ListElement {
|
ListElement {
|
||||||
title: "StatusCommunityCard"
|
title: "StatusCommunityCard"
|
||||||
section: "Panels"
|
section: "Panels"
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
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 AppLayouts.Chat.views.communities 1.0
|
||||||
|
|
||||||
|
import Storybook 1.0
|
||||||
|
import Models 1.0
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
import shared.views.chat 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
|
||||||
|
|
||||||
|
RowLayout {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: menu1
|
||||||
|
MessageContextMenuView {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
hideDisabledItems: false
|
||||||
|
onClosed: {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: menu2
|
||||||
|
MessageContextMenuView {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
hideDisabledItems: true
|
||||||
|
onClosed: {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
ScrollView {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
ColumnLayout {
|
||||||
|
spacing: 16
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -66,7 +66,6 @@ SplitView {
|
||||||
|
|
||||||
sourceComponent: UserListPanel {
|
sourceComponent: UserListPanel {
|
||||||
usersModel: model
|
usersModel: model
|
||||||
messageContextMenu: null
|
|
||||||
label: "Some label"
|
label: "Some label"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,7 +71,7 @@ Menu {
|
||||||
closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape
|
closePolicy: Popup.CloseOnPressOutside | Popup.CloseOnEscape
|
||||||
topPadding: 8
|
topPadding: 8
|
||||||
bottomPadding: 8
|
bottomPadding: 8
|
||||||
bottomMargin: 16
|
margins: 16
|
||||||
|
|
||||||
onOpened: {
|
onOpened: {
|
||||||
if (typeof openHandler === "function") {
|
if (typeof openHandler === "function") {
|
||||||
|
|
|
@ -8,16 +8,16 @@ import StatusQ.Components 0.1
|
||||||
import shared 1.0
|
import shared 1.0
|
||||||
import shared.panels 1.0
|
import shared.panels 1.0
|
||||||
import shared.status 1.0
|
import shared.status 1.0
|
||||||
|
import shared.views.chat 1.0
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
|
||||||
import SortFilterProxyModel 0.2
|
import SortFilterProxyModel 0.2
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: root
|
id: root
|
||||||
anchors.fill: parent
|
|
||||||
|
|
||||||
|
property var store
|
||||||
property var usersModel
|
property var usersModel
|
||||||
property var messageContextMenu
|
|
||||||
property string label
|
property string label
|
||||||
|
|
||||||
StatusBaseText {
|
StatusBaseText {
|
||||||
|
@ -98,15 +98,13 @@ Item {
|
||||||
ringSettings.ringSpecModel: model.colorHash
|
ringSettings.ringSpecModel: model.colorHash
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (mouse.button === Qt.RightButton) {
|
if (mouse.button === Qt.RightButton) {
|
||||||
// Set parent, X & Y positions for the messageContextMenu
|
Global.openMenu(profileContextMenuComponent, this, {
|
||||||
messageContextMenu.parent = this
|
myPublicKey: userProfile.pubKey,
|
||||||
messageContextMenu.isProfile = true
|
selectedUserPublicKey: model.pubKey,
|
||||||
messageContextMenu.myPublicKey = userProfile.pubKey
|
selectedUserDisplayName: title,
|
||||||
messageContextMenu.selectedUserPublicKey = model.pubKey
|
selectedUserIcon: model.icon,
|
||||||
messageContextMenu.selectedUserDisplayName = title
|
})
|
||||||
messageContextMenu.selectedUserIcon = model.icon
|
} else if (mouse.button === Qt.LeftButton) {
|
||||||
messageContextMenu.popup(4, 4)
|
|
||||||
} else if (mouse.button === Qt.LeftButton && !!messageContextMenu) {
|
|
||||||
Global.openProfilePopup(model.pubKey);
|
Global.openProfilePopup(model.pubKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,4 +134,23 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: profileContextMenuComponent
|
||||||
|
|
||||||
|
ProfileContextMenu {
|
||||||
|
store: root.store
|
||||||
|
margins: 8
|
||||||
|
onOpenProfileClicked: {
|
||||||
|
Global.openProfilePopup(publicKey, null)
|
||||||
|
}
|
||||||
|
onCreateOneToOneChat: {
|
||||||
|
Global.changeAppSectionBySectionType(Constants.appSection.chat)
|
||||||
|
root.store.chatCommunitySectionModule.createOneToOneChat(communityId, chatId, ensName)
|
||||||
|
}
|
||||||
|
onClosed: {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,12 +21,10 @@ SettingsPageLayout {
|
||||||
property var pendingMemberRequestsModel
|
property var pendingMemberRequestsModel
|
||||||
property var declinedMemberRequestsModel
|
property var declinedMemberRequestsModel
|
||||||
property string communityName
|
property string communityName
|
||||||
property var communityMemberContextMenu
|
|
||||||
|
|
||||||
property bool editable: true
|
property bool editable: true
|
||||||
|
|
||||||
signal membershipRequestsClicked()
|
signal membershipRequestsClicked()
|
||||||
signal userProfileClicked(string id)
|
|
||||||
signal kickUserClicked(string id)
|
signal kickUserClicked(string id)
|
||||||
signal banUserClicked(string id)
|
signal banUserClicked(string id)
|
||||||
signal unbanUserClicked(string id)
|
signal unbanUserClicked(string id)
|
||||||
|
@ -120,7 +118,6 @@ SettingsPageLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
onUserProfileClicked: root.userProfileClicked(id)
|
|
||||||
onKickUserClicked: {
|
onKickUserClicked: {
|
||||||
kickModal.userNameToKick = name
|
kickModal.userNameToKick = name
|
||||||
kickModal.userIdToKick = id
|
kickModal.userIdToKick = id
|
||||||
|
@ -151,7 +148,6 @@ SettingsPageLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
onUserProfileClicked: root.userProfileClicked(id)
|
|
||||||
onAcceptRequestToJoin: root.acceptRequestToJoin(id)
|
onAcceptRequestToJoin: root.acceptRequestToJoin(id)
|
||||||
onDeclineRequestToJoin: root.declineRequestToJoin(id)
|
onDeclineRequestToJoin: root.declineRequestToJoin(id)
|
||||||
}
|
}
|
||||||
|
@ -173,7 +169,6 @@ SettingsPageLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
onUserProfileClicked: root.userProfileClicked(id)
|
|
||||||
onAcceptRequestToJoin: root.acceptRequestToJoin(id)
|
onAcceptRequestToJoin: root.acceptRequestToJoin(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +189,6 @@ SettingsPageLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.fillHeight: true
|
Layout.fillHeight: true
|
||||||
|
|
||||||
onUserProfileClicked: root.userProfileClicked(id)
|
|
||||||
onUnbanUserClicked: root.unbanUserClicked(id)
|
onUnbanUserClicked: root.unbanUserClicked(id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ import StatusQ.Components 0.1
|
||||||
import StatusQ.Popups 0.1
|
import StatusQ.Popups 0.1
|
||||||
|
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
import shared.views.chat 1.0
|
||||||
import shared.controls.chat 1.0
|
import shared.controls.chat 1.0
|
||||||
|
|
||||||
import "../../layouts"
|
import "../../layouts"
|
||||||
|
@ -20,7 +21,6 @@ Item {
|
||||||
property var model
|
property var model
|
||||||
property var communityMemberContextMenu
|
property var communityMemberContextMenu
|
||||||
|
|
||||||
signal userProfileClicked(string id)
|
|
||||||
signal kickUserClicked(string id, string name)
|
signal kickUserClicked(string id, string name)
|
||||||
signal banUserClicked(string id, string name)
|
signal banUserClicked(string id, string name)
|
||||||
signal unbanUserClicked(string id)
|
signal unbanUserClicked(string id)
|
||||||
|
@ -146,18 +146,37 @@ Item {
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if(mouse.button === Qt.RightButton) {
|
if(mouse.button === Qt.RightButton) {
|
||||||
// Set parent, X & Y positions for the messageContextMenu
|
Global.openMenu(memberContextMenuComponent, this, {
|
||||||
root.communityMemberContextMenu.parent = this
|
selectedUserPublicKey: model.pubKey,
|
||||||
root.communityMemberContextMenu.isProfile = true
|
selectedUserDisplayName: userName,
|
||||||
root.communityMemberContextMenu.selectedUserPublicKey = model.pubKey
|
selectedUserIcon: asset.name,
|
||||||
root.communityMemberContextMenu.selectedUserDisplayName = userName
|
})
|
||||||
root.communityMemberContextMenu.selectedUserIcon = asset.name
|
|
||||||
root.communityMemberContextMenu.popup()
|
|
||||||
} else {
|
} else {
|
||||||
root.userProfileClicked(model.pubKey)
|
Global.openProfilePopup(model.pubKey)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: memberContextMenuComponent
|
||||||
|
|
||||||
|
ProfileContextMenu {
|
||||||
|
id: memberContextMenuView
|
||||||
|
store: root.rootStore
|
||||||
|
myPublicKey: root.rootStore.myPublicKey()
|
||||||
|
|
||||||
|
onOpenProfileClicked: {
|
||||||
|
Global.openProfilePopup(publicKey, null)
|
||||||
|
}
|
||||||
|
onCreateOneToOneChat: {
|
||||||
|
Global.changeAppSectionBySectionType(Constants.appSection.chat)
|
||||||
|
root.rootStore.chatCommunitySectionModule.createOneToOneChat(communityId, chatId, ensName)
|
||||||
|
}
|
||||||
|
onClosed: {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import QtGraphicalEffects 1.14
|
||||||
import StatusQ.Core 0.1
|
import StatusQ.Core 0.1
|
||||||
import StatusQ.Core.Theme 0.1
|
import StatusQ.Core.Theme 0.1
|
||||||
import StatusQ.Controls 0.1
|
import StatusQ.Controls 0.1
|
||||||
|
import StatusQ.Popups 0.1
|
||||||
import StatusQ.Popups.Dialog 0.1
|
import StatusQ.Popups.Dialog 0.1
|
||||||
|
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
|
@ -33,6 +34,15 @@ StatusDialog {
|
||||||
subtitle: root.messageToPin ? qsTr("Unpin a previous message first")
|
subtitle: root.messageToPin ? qsTr("Unpin a previous message first")
|
||||||
: qsTr("%n message(s)", "", pinnedMessageListView.count)
|
: qsTr("%n message(s)", "", pinnedMessageListView.count)
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
id: d
|
||||||
|
|
||||||
|
function jumpToMessage(messageId) {
|
||||||
|
root.close()
|
||||||
|
root.messageStore.messageModule.jumpToMessage(messageId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
contentItem: ColumnLayout {
|
contentItem: ColumnLayout {
|
||||||
id: column
|
id: column
|
||||||
|
|
||||||
|
@ -67,7 +77,6 @@ StatusDialog {
|
||||||
|
|
||||||
rootStore: root.store
|
rootStore: root.store
|
||||||
messageStore: root.messageStore
|
messageStore: root.messageStore
|
||||||
messageContextMenu: msgContextMenu
|
|
||||||
|
|
||||||
messageId: model.id
|
messageId: model.id
|
||||||
responseToMessageWithId: model.responseToMessageWithId
|
responseToMessageWithId: model.responseToMessageWithId
|
||||||
|
@ -120,13 +129,22 @@ StatusDialog {
|
||||||
hoverEnabled: true
|
hoverEnabled: true
|
||||||
cursorShape: Qt.PointingHandCursor
|
cursorShape: Qt.PointingHandCursor
|
||||||
z: 55
|
z: 55
|
||||||
onClicked: {
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
|
onClicked: (mouse) => {
|
||||||
|
switch (mouse.button) {
|
||||||
|
case Qt.RightButton:
|
||||||
|
Global.openMenu(pinnedPopupMessageContextMenuComponent, this, {
|
||||||
|
messageId: messageItem.messageId,
|
||||||
|
})
|
||||||
|
break
|
||||||
|
case Qt.LeftButton:
|
||||||
if (!!root.messageToPin) {
|
if (!!root.messageToPin) {
|
||||||
if (!radio.checked)
|
if (!radio.checked)
|
||||||
radio.checked = true
|
radio.checked = true
|
||||||
} else {
|
} else {
|
||||||
root.close()
|
d.jumpToMessage(model.id)
|
||||||
root.messageStore.messageModule.jumpToMessage(model.id)
|
}
|
||||||
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,24 +178,42 @@ StatusDialog {
|
||||||
root.messageToUnpin = checked ? model.id : ""
|
root.messageToUnpin = checked ? model.id : ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: pinnedPopupMessageContextMenuComponent
|
||||||
|
|
||||||
|
StatusMenu {
|
||||||
|
id: messageContextMenu
|
||||||
|
|
||||||
|
property string messageId
|
||||||
|
|
||||||
|
StatusAction {
|
||||||
|
text: qsTr("Unpin")
|
||||||
|
icon.name: "unpin"
|
||||||
|
onTriggered: {
|
||||||
|
root.messageStore.unpinMessage(messageContextMenu.messageId)
|
||||||
|
close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageContextMenuView {
|
StatusAction {
|
||||||
id: msgContextMenu
|
text: qsTr("Jump to")
|
||||||
store: root.store
|
icon.name: "arrow-up"
|
||||||
pinnedPopup: true
|
onTriggered: {
|
||||||
pinnedMessage: true
|
d.jumpToMessage(messageContextMenu.messageId)
|
||||||
onShouldCloseParentPopup: {
|
close()
|
||||||
root.close()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onUnpinMessage: {
|
onOpened: {
|
||||||
root.messageStore.unpinMessage(messageId)
|
messageItem.setMessageActive(model.id, true)
|
||||||
|
}
|
||||||
|
onClosed: {
|
||||||
|
messageItem.setMessageActive(model.id, false)
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onJumpToMessage: {
|
|
||||||
root.messageStore.messageModule.jumpToMessage(messageId)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -124,6 +124,13 @@ QtObject {
|
||||||
messageModule.deleteMessage(messageId)
|
messageModule.deleteMessage(messageId)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function warnAndDeleteMessage(messageId) {
|
||||||
|
if (localAccountSensitiveSettings.showDeleteMessageWarning)
|
||||||
|
Global.openDeleteMessagePopup(messageId, this)
|
||||||
|
else
|
||||||
|
deleteMessage(messageId)
|
||||||
|
}
|
||||||
|
|
||||||
function setEditModeOn(messageId) {
|
function setEditModeOn(messageId) {
|
||||||
if(!messageModule)
|
if(!messageModule)
|
||||||
return
|
return
|
||||||
|
|
|
@ -209,18 +209,11 @@ QtObject {
|
||||||
stickersModuleInst.send(channelId, hash, replyTo, pack, url)
|
stickersModuleInst.send(channelId, hash, replyTo, pack, url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: This seems to be better in Utils.qml
|
||||||
function copyToClipboard(text) {
|
function copyToClipboard(text) {
|
||||||
globalUtilsInst.copyToClipboard(text)
|
globalUtilsInst.copyToClipboard(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
function copyImageToClipboardByUrl(content) {
|
|
||||||
globalUtilsInst.copyImageToClipboardByUrl(content)
|
|
||||||
}
|
|
||||||
|
|
||||||
function downloadImageByUrl(url, path) {
|
|
||||||
globalUtilsInst.downloadImageByUrl(url, path)
|
|
||||||
}
|
|
||||||
|
|
||||||
function isCurrentUser(pubkey) {
|
function isCurrentUser(pubkey) {
|
||||||
return userProfileInst.pubKey === pubkey
|
return userProfileInst.pubKey === pubkey
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,53 +74,10 @@ ColumnLayout {
|
||||||
chatSectionModule: root.rootStore.chatCommunitySectionModule
|
chatSectionModule: root.rootStore.chatCommunitySectionModule
|
||||||
}
|
}
|
||||||
|
|
||||||
Loader {
|
QtObject {
|
||||||
id: contextMenuLoader
|
id: d
|
||||||
active: root.isActiveChannel
|
|
||||||
asynchronous: true
|
|
||||||
|
|
||||||
// FIXME: `MessageContextMenuView` is way too heavy
|
function showReplyArea(messageId) {
|
||||||
// see: https://github.com/status-im/status-desktop/pull/10343#issuecomment-1515103756
|
|
||||||
sourceComponent: MessageContextMenuView {
|
|
||||||
store: root.rootStore
|
|
||||||
reactionModel: root.rootStore.emojiReactionsModel
|
|
||||||
disabledForChat: !root.isUserAllowedToSendMessage
|
|
||||||
|
|
||||||
onPinMessage: {
|
|
||||||
messageStore.pinMessage(messageId)
|
|
||||||
}
|
|
||||||
|
|
||||||
onUnpinMessage: {
|
|
||||||
messageStore.unpinMessage(messageId)
|
|
||||||
}
|
|
||||||
|
|
||||||
onPinnedMessagesLimitReached: {
|
|
||||||
if(!chatContentModule) {
|
|
||||||
console.warn("error on open pinned messages limit reached from message context menu - chat content module is not set")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
Global.openPinnedMessagesPopupRequested(rootStore, messageStore, chatContentModule.pinnedMessagesModel, messageId, root.chatId)
|
|
||||||
}
|
|
||||||
|
|
||||||
onToggleReaction: {
|
|
||||||
messageStore.toggleReaction(messageId, emojiId)
|
|
||||||
}
|
|
||||||
|
|
||||||
onOpenProfileClicked: {
|
|
||||||
Global.openProfilePopup(publicKey, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
onDeleteMessage: {
|
|
||||||
messageStore.deleteMessage(messageId)
|
|
||||||
}
|
|
||||||
|
|
||||||
onEditClicked: messageStore.setEditModeOn(messageId)
|
|
||||||
|
|
||||||
onCreateOneToOneChat: {
|
|
||||||
Global.changeAppSectionBySectionType(Constants.appSection.chat)
|
|
||||||
root.rootStore.chatCommunitySectionModule.createOneToOneChat("", chatId, ensName)
|
|
||||||
}
|
|
||||||
onShowReplyArea: {
|
|
||||||
let obj = messageStore.getMessageByIdAsJson(messageId)
|
let obj = messageStore.getMessageByIdAsJson(messageId)
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
return
|
return
|
||||||
|
@ -130,7 +87,6 @@ ColumnLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
|
@ -146,30 +102,26 @@ ColumnLayout {
|
||||||
chatContentModule: root.chatContentModule
|
chatContentModule: root.chatContentModule
|
||||||
rootStore: root.rootStore
|
rootStore: root.rootStore
|
||||||
contactsStore: root.contactsStore
|
contactsStore: root.contactsStore
|
||||||
messageContextMenu: contextMenuLoader.item
|
|
||||||
messageStore: root.messageStore
|
messageStore: root.messageStore
|
||||||
emojiPopup: root.emojiPopup
|
emojiPopup: root.emojiPopup
|
||||||
stickersPopup: root.stickersPopup
|
stickersPopup: root.stickersPopup
|
||||||
usersStore: root.usersStore
|
usersStore: root.usersStore
|
||||||
stickersLoaded: root.stickersLoaded
|
stickersLoaded: root.stickersLoaded
|
||||||
publicKey: root.chatId
|
chatId: root.chatId
|
||||||
isOneToOne: root.chatType === Constants.chatType.oneToOne
|
isOneToOne: root.chatType === Constants.chatType.oneToOne
|
||||||
isChatBlocked: root.isBlocked || !root.isUserAllowedToSendMessage
|
isChatBlocked: root.isBlocked || !root.isUserAllowedToSendMessage
|
||||||
channelEmoji: !chatContentModule ? "" : (chatContentModule.chatDetails.emoji || "")
|
channelEmoji: !chatContentModule ? "" : (chatContentModule.chatDetails.emoji || "")
|
||||||
isActiveChannel: root.isActiveChannel
|
isActiveChannel: root.isActiveChannel
|
||||||
onShowReplyArea: {
|
onShowReplyArea: (messageId, senderId) => {
|
||||||
let obj = messageStore.getMessageByIdAsJson(messageId)
|
d.showReplyArea(messageId)
|
||||||
if (!obj) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if (inputAreaLoader.item) {
|
|
||||||
inputAreaLoader.item.chatInput.showReplyArea(messageId, obj.senderDisplayName, obj.messageText, obj.contentType, obj.messageImage, obj.albumMessageImages, obj.albumImagesCount, obj.sticker)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
onOpenStickerPackPopup: {
|
onOpenStickerPackPopup: {
|
||||||
root.openStickerPackPopup(stickerPackId);
|
root.openStickerPackPopup(stickerPackId);
|
||||||
}
|
}
|
||||||
onEditModeChanged: if (!editModeOn && inputAreaLoader.item) inputAreaLoader.item.chatInput.forceInputActiveFocus()
|
onEditModeChanged: {
|
||||||
|
if (!editModeOn && inputAreaLoader.item)
|
||||||
|
inputAreaLoader.item.chatInput.forceInputActiveFocus()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -214,7 +166,6 @@ ColumnLayout {
|
||||||
|
|
||||||
textInput.text: inputAreaLoader.preservedText
|
textInput.text: inputAreaLoader.preservedText
|
||||||
textInput.placeholderText: root.chatInputPlaceholder
|
textInput.placeholderText: root.chatInputPlaceholder
|
||||||
messageContextMenu: contextMenuLoader.item
|
|
||||||
emojiPopup: root.emojiPopup
|
emojiPopup: root.emojiPopup
|
||||||
stickersPopup: root.stickersPopup
|
stickersPopup: root.stickersPopup
|
||||||
isContactBlocked: root.isBlocked
|
isContactBlocked: root.isBlocked
|
||||||
|
|
|
@ -33,15 +33,13 @@ Item {
|
||||||
property var emojiPopup
|
property var emojiPopup
|
||||||
property var stickersPopup
|
property var stickersPopup
|
||||||
|
|
||||||
property string publicKey: ""
|
property string chatId: ""
|
||||||
property bool stickersLoaded: false
|
property bool stickersLoaded: false
|
||||||
property alias chatLogView: chatLogView
|
property alias chatLogView: chatLogView
|
||||||
property bool isChatBlocked: false
|
property bool isChatBlocked: false
|
||||||
property bool isOneToOne: false
|
property bool isOneToOne: false
|
||||||
property bool isActiveChannel: false
|
property bool isActiveChannel: false
|
||||||
|
|
||||||
property var messageContextMenu
|
|
||||||
|
|
||||||
signal openStickerPackPopup(string stickerPackId)
|
signal openStickerPackPopup(string stickerPackId)
|
||||||
signal showReplyArea(string messageId, string author)
|
signal showReplyArea(string messageId, string author)
|
||||||
signal editModeChanged(bool editModeOn)
|
signal editModeChanged(bool editModeOn)
|
||||||
|
@ -250,11 +248,12 @@ Item {
|
||||||
emojiPopup: root.emojiPopup
|
emojiPopup: root.emojiPopup
|
||||||
stickersPopup: root.stickersPopup
|
stickersPopup: root.stickersPopup
|
||||||
chatLogView: ListView.view
|
chatLogView: ListView.view
|
||||||
|
chatContentModule: root.chatContentModule
|
||||||
|
|
||||||
isActiveChannel: root.isActiveChannel
|
isActiveChannel: root.isActiveChannel
|
||||||
isChatBlocked: root.isChatBlocked
|
isChatBlocked: root.isChatBlocked
|
||||||
messageContextMenu: root.messageContextMenu
|
|
||||||
|
|
||||||
|
chatId: root.chatId
|
||||||
messageId: model.id
|
messageId: model.id
|
||||||
communityId: model.communityId
|
communityId: model.communityId
|
||||||
responseToMessageWithId: model.responseToMessageWithId
|
responseToMessageWithId: model.responseToMessageWithId
|
||||||
|
@ -322,8 +321,6 @@ Item {
|
||||||
root.showReplyArea(messageId, author)
|
root.showReplyArea(messageId, author)
|
||||||
}
|
}
|
||||||
|
|
||||||
onImageClicked: Global.openImagePopup(image, messageContextMenu)
|
|
||||||
|
|
||||||
stickersLoaded: root.stickersLoaded
|
stickersLoaded: root.stickersLoaded
|
||||||
|
|
||||||
onVisibleChanged: {
|
onVisibleChanged: {
|
||||||
|
@ -364,7 +361,7 @@ Item {
|
||||||
anchors.horizontalCenter: parent.horizontalCenter
|
anchors.horizontalCenter: parent.horizontalCenter
|
||||||
text: qsTr("Send Contact Request")
|
text: qsTr("Send Contact Request")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
Global.openContactRequestPopup(root.publicKey, null)
|
Global.openContactRequestPopup(root.chatId, null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -379,14 +376,14 @@ Item {
|
||||||
text: qsTr("Reject Contact Request")
|
text: qsTr("Reject Contact Request")
|
||||||
type: StatusBaseButton.Type.Danger
|
type: StatusBaseButton.Type.Danger
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.contactsStore.dismissContactRequest(root.publicKey, "")
|
root.contactsStore.dismissContactRequest(root.chatId, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusButton {
|
StatusButton {
|
||||||
text: qsTr("Accept Contact Request")
|
text: qsTr("Accept Contact Request")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.contactsStore.acceptContactRequest(root.publicKey, "")
|
root.contactsStore.acceptContactRequest(root.chatId, "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -112,8 +112,9 @@ StatusSectionLayout {
|
||||||
rightPanel: Component {
|
rightPanel: Component {
|
||||||
id: userListComponent
|
id: userListComponent
|
||||||
UserListPanel {
|
UserListPanel {
|
||||||
|
anchors.fill: parent
|
||||||
|
store: root.rootStore
|
||||||
label: qsTr("Members")
|
label: qsTr("Members")
|
||||||
messageContextMenu: quickActionMessageOptionsMenu
|
|
||||||
usersModel: {
|
usersModel: {
|
||||||
let chatContentModule = root.rootStore.currentChatContentModule()
|
let chatContentModule = root.rootStore.currentChatContentModule()
|
||||||
if (!chatContentModule || !chatContentModule.usersModule) {
|
if (!chatContentModule || !chatContentModule.usersModule) {
|
||||||
|
@ -166,17 +167,4 @@ StatusSectionLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageContextMenuView {
|
|
||||||
id: quickActionMessageOptionsMenu
|
|
||||||
store: root.rootStore
|
|
||||||
|
|
||||||
onOpenProfileClicked: {
|
|
||||||
Global.openProfilePopup(publicKey, null)
|
|
||||||
}
|
|
||||||
onCreateOneToOneChat: {
|
|
||||||
Global.changeAppSectionBySectionType(Constants.appSection.chat)
|
|
||||||
root.rootStore.chatCommunitySectionModule.createOneToOneChat(communityId, chatId, ensName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,9 +221,7 @@ StatusSectionLayout {
|
||||||
declinedMemberRequestsModel: root.community.declinedMemberRequests
|
declinedMemberRequestsModel: root.community.declinedMemberRequests
|
||||||
editable: root.community.amISectionAdmin
|
editable: root.community.amISectionAdmin
|
||||||
communityName: root.community.name
|
communityName: root.community.name
|
||||||
communityMemberContextMenu: memberContextMenuView
|
|
||||||
|
|
||||||
onUserProfileClicked: Global.openProfilePopup(id)
|
|
||||||
onKickUserClicked: root.rootStore.removeUserFromCommunity(id)
|
onKickUserClicked: root.rootStore.removeUserFromCommunity(id)
|
||||||
onBanUserClicked: root.rootStore.banUserFromCommunity(id)
|
onBanUserClicked: root.rootStore.banUserFromCommunity(id)
|
||||||
onUnbanUserClicked: root.rootStore.unbanUserFromCommunity(id)
|
onUnbanUserClicked: root.rootStore.unbanUserFromCommunity(id)
|
||||||
|
@ -485,22 +483,6 @@ StatusSectionLayout {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageContextMenuView {
|
|
||||||
id: memberContextMenuView
|
|
||||||
store: root.rootStore
|
|
||||||
isProfile: true
|
|
||||||
amIChatAdmin: root.rootStore.amIChatAdmin()
|
|
||||||
myPublicKey: root.rootStore.myPublicKey()
|
|
||||||
|
|
||||||
onOpenProfileClicked: {
|
|
||||||
Global.openProfilePopup(publicKey, null)
|
|
||||||
}
|
|
||||||
onCreateOneToOneChat: {
|
|
||||||
Global.changeAppSectionBySectionType(Constants.appSection.chat)
|
|
||||||
root.rootStore.chatCommunitySectionModule.createOneToOneChat(communityId, chatId, ensName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Connections {
|
Connections {
|
||||||
target: root.chatCommunitySectionModule
|
target: root.chatCommunitySectionModule
|
||||||
function onOpenNoPermissionsToJoinPopup(communityName: string, userName: string, communityId: string, requestId: string) {
|
function onOpenNoPermissionsToJoinPopup(communityName: string, userName: string, communityId: string, requestId: string) {
|
||||||
|
|
|
@ -34,10 +34,11 @@ SettingsContentBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
function openContextMenu(publicKey, name, icon) {
|
function openContextMenu(publicKey, name, icon) {
|
||||||
contactContextMenu.selectedUserPublicKey = publicKey
|
Global.openMenu(contactContextMenuComponent, this, {
|
||||||
contactContextMenu.selectedUserDisplayName = name
|
selectedUserPublicKey: publicKey,
|
||||||
contactContextMenu.selectedUserIcon = icon
|
selectedUserDisplayName: name,
|
||||||
contactContextMenu.popup()
|
selectedUserIcon: icon,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -46,18 +47,23 @@ SettingsContentBase {
|
||||||
height: (searchBox.height + contactsTabBar.height
|
height: (searchBox.height + contactsTabBar.height
|
||||||
+ stackLayout.height + (2 * Style.current.bigPadding))
|
+ stackLayout.height + (2 * Style.current.bigPadding))
|
||||||
|
|
||||||
MessageContextMenuView {
|
Component {
|
||||||
|
id: contactContextMenuComponent
|
||||||
|
|
||||||
|
ProfileContextMenu {
|
||||||
id: contactContextMenu
|
id: contactContextMenu
|
||||||
store: ({contactsStore: root.contactsStore})
|
store: ({contactsStore: root.contactsStore})
|
||||||
isProfile: true
|
|
||||||
|
|
||||||
onOpenProfileClicked: function (pubkey) {
|
onOpenProfileClicked: function (pubkey) {
|
||||||
Global.openProfilePopup(pubkey, null)
|
Global.openProfilePopup(pubkey, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
onCreateOneToOneChat: function (communityId, chatId, ensName) {
|
onCreateOneToOneChat: function (communityId, chatId, ensName) {
|
||||||
root.contactsStore.joinPrivateChat(chatId)
|
root.contactsStore.joinPrivateChat(chatId)
|
||||||
}
|
}
|
||||||
|
onClosed: {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SearchBox {
|
SearchBox {
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import QtQuick 2.15
|
import QtQuick 2.15
|
||||||
|
import QtQuick.Dialogs 1.0
|
||||||
|
|
||||||
import AppLayouts.Chat.popups 1.0
|
import AppLayouts.Chat.popups 1.0
|
||||||
import AppLayouts.Profile.popups 1.0
|
import AppLayouts.Profile.popups 1.0
|
||||||
|
@ -40,6 +41,8 @@ QtObject {
|
||||||
Global.importCommunityPopupRequested.connect(openImportCommunityPopup)
|
Global.importCommunityPopupRequested.connect(openImportCommunityPopup)
|
||||||
Global.removeContactRequested.connect(openRemoveContactConfirmationPopup)
|
Global.removeContactRequested.connect(openRemoveContactConfirmationPopup)
|
||||||
Global.openPopupRequested.connect(openPopup)
|
Global.openPopupRequested.connect(openPopup)
|
||||||
|
Global.openDeleteMessagePopup.connect(openDeleteMessagePopup)
|
||||||
|
Global.openDownloadImageDialog.connect(openDownloadImageDialog)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openPopup(popupComponent, params = {}, cb = null) {
|
function openPopup(popupComponent, params = {}, cb = null) {
|
||||||
|
@ -77,9 +80,8 @@ QtObject {
|
||||||
openPopup(downloadPageComponent, popupProperties)
|
openPopup(downloadPageComponent, popupProperties)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openImagePopup(image, contextMenu) {
|
function openImagePopup(image) {
|
||||||
var popup = imagePopupComponent.createObject(popupParent)
|
var popup = imagePopupComponent.createObject(popupParent)
|
||||||
popup.contextMenu = contextMenu
|
|
||||||
popup.openPopup(image)
|
popup.openPopup(image)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -206,6 +208,21 @@ QtObject {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openDeleteMessagePopup(messageId, messageStore) {
|
||||||
|
openPopup(deleteMessageConfirmationDialogComponent,
|
||||||
|
{
|
||||||
|
messageId,
|
||||||
|
messageStore
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDownloadImageDialog(imageSource) {
|
||||||
|
// We don't use `openPopup`, because there's no `FileDialog::closed` signal.
|
||||||
|
// And multiple file dialogs are (almost) ok
|
||||||
|
const popup = downloadImageDialogComponent.createObject(popupParent, { imageSource })
|
||||||
|
popup.open()
|
||||||
|
}
|
||||||
|
|
||||||
readonly property list<Component> _components: [
|
readonly property list<Component> _components: [
|
||||||
Component {
|
Component {
|
||||||
id: removeContactConfirmationDialog
|
id: removeContactConfirmationDialog
|
||||||
|
@ -316,17 +333,6 @@ QtObject {
|
||||||
id: imagePopupComponent
|
id: imagePopupComponent
|
||||||
StatusImageModal {
|
StatusImageModal {
|
||||||
id: imagePopup
|
id: imagePopup
|
||||||
onClicked: {
|
|
||||||
if (mouse.button === Qt.LeftButton) {
|
|
||||||
imagePopup.close()
|
|
||||||
} else if(mouse.button === Qt.RightButton) {
|
|
||||||
contextMenu.imageSource = imagePopup.imageSource
|
|
||||||
contextMenu.hideEmojiPicker = true
|
|
||||||
contextMenu.isRightClickOnImage = true
|
|
||||||
contextMenu.parent = imagePopup.contentItem
|
|
||||||
contextMenu.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onClosed: destroy()
|
onClosed: destroy()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -461,6 +467,36 @@ QtObject {
|
||||||
DiscordImportProgressDialog {
|
DiscordImportProgressDialog {
|
||||||
store: root.communitiesStore
|
store: root.communitiesStore
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: deleteMessageConfirmationDialogComponent
|
||||||
|
DeleteMessageConfirmationPopup {
|
||||||
|
onClosed: destroy()
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: downloadImageDialogComponent
|
||||||
|
FileDialog {
|
||||||
|
property string imageSource
|
||||||
|
title: qsTr("Please choose a directory")
|
||||||
|
selectFolder: true
|
||||||
|
selectExisting: true
|
||||||
|
selectMultiple: false
|
||||||
|
modality: Qt.NonModal
|
||||||
|
onAccepted: {
|
||||||
|
Utils.downloadImageByUrl(imageSource, fileUrl)
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
onRejected: {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
Component.onCompleted: {
|
||||||
|
open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
import shared 1.0
|
||||||
|
|
||||||
|
Row {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var reactionsModel
|
||||||
|
property var messageReactionsModel: [] // TODO: https://github.com/status-im/status-desktop/issues/10703
|
||||||
|
|
||||||
|
signal toggleReaction(int emojiId)
|
||||||
|
|
||||||
|
spacing: Style.current.halfPadding
|
||||||
|
leftPadding: Style.current.halfPadding
|
||||||
|
rightPadding: Style.current.halfPadding
|
||||||
|
|
||||||
|
Repeater {
|
||||||
|
model: root.reactionsModel
|
||||||
|
delegate: EmojiReaction {
|
||||||
|
source: Style.svg(filename)
|
||||||
|
emojiId: model.emojiId
|
||||||
|
// reactedByUser: !!root.messageReactionsModel[emojiId]
|
||||||
|
onCloseModal: {
|
||||||
|
root.toggleReaction(emojiId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -138,7 +138,7 @@ Item {
|
||||||
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!!root.store.profileLargeImage)
|
if (!!root.store.profileLargeImage)
|
||||||
imageEditMenu.popup(this, mouse.x, mouse.y);
|
Global.openMenu(editImageMenuComponent, this)
|
||||||
else
|
else
|
||||||
Global.openChangeProfilePicPopup(tempIcon);
|
Global.openChangeProfilePicPopup(tempIcon);
|
||||||
}
|
}
|
||||||
|
@ -271,8 +271,10 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: editImageMenuComponent
|
||||||
|
|
||||||
StatusMenu {
|
StatusMenu {
|
||||||
id: imageEditMenu
|
|
||||||
width: 200
|
width: 200
|
||||||
|
|
||||||
StatusAction {
|
StatusAction {
|
||||||
|
@ -298,3 +300,4 @@ Item {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,3 +14,4 @@ MessageBorder 1.0 MessageBorder.qml
|
||||||
EmojiReaction 1.0 EmojiReaction.qml
|
EmojiReaction 1.0 EmojiReaction.qml
|
||||||
ProfileHeader 1.0 ProfileHeader.qml
|
ProfileHeader 1.0 ProfileHeader.qml
|
||||||
VerificationLabel 1.0 VerificationLabel.qml
|
VerificationLabel 1.0 VerificationLabel.qml
|
||||||
|
MessageReactionsRow 1.0 MessageReactionsRow.qml
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import QtQuick 2.15
|
||||||
|
|
||||||
|
ConfirmationDialog {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property var messageStore
|
||||||
|
property string messageId
|
||||||
|
|
||||||
|
header.title: qsTr("Confirm deleting this message")
|
||||||
|
confirmationText: qsTr("Are you sure you want to delete this message? Be aware that other clients are not guaranteed to delete the message as well.")
|
||||||
|
height: 260
|
||||||
|
checkbox.visible: true
|
||||||
|
confirmButtonObjectName: "chatButtonsPanelConfirmDeleteMessageButton"
|
||||||
|
|
||||||
|
executeConfirm: () => {
|
||||||
|
if (checkbox.checked) {
|
||||||
|
localAccountSensitiveSettings.showDeleteMessageWarning = false
|
||||||
|
}
|
||||||
|
close()
|
||||||
|
messageStore.deleteMessage(messageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
onClosed: {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,3 +27,4 @@ AccountsModalHeader 1.0 AccountsModalHeader.qml
|
||||||
GetSyncCodeInstructionsPopup 1.0 GetSyncCodeInstructionsPopup.qml
|
GetSyncCodeInstructionsPopup 1.0 GetSyncCodeInstructionsPopup.qml
|
||||||
NoPermissionsToJoinPopup 1.0 NoPermissionsToJoinPopup.qml
|
NoPermissionsToJoinPopup 1.0 NoPermissionsToJoinPopup.qml
|
||||||
RemoveAccountConfirmationPopup 1.0 RemoveAccountConfirmationPopup.qml
|
RemoveAccountConfirmationPopup 1.0 RemoveAccountConfirmationPopup.qml
|
||||||
|
DeleteMessageConfirmationPopup 1.0 DeleteMessageConfirmationPopup.qml
|
||||||
|
|
|
@ -63,8 +63,6 @@ Rectangle {
|
||||||
|
|
||||||
property var imageErrorMessageLocation: StatusChatInput.ImageErrorMessageLocation.Top // TODO: Remove this proeprty?
|
property var imageErrorMessageLocation: StatusChatInput.ImageErrorMessageLocation.Top // TODO: Remove this proeprty?
|
||||||
|
|
||||||
property var messageContextMenu
|
|
||||||
|
|
||||||
property alias suggestions: suggestionsBox
|
property alias suggestions: suggestionsBox
|
||||||
|
|
||||||
enum ImageErrorMessageLocation {
|
enum ImageErrorMessageLocation {
|
||||||
|
@ -1226,7 +1224,9 @@ Rectangle {
|
||||||
Layout.leftMargin: Style.current.halfPadding
|
Layout.leftMargin: Style.current.halfPadding
|
||||||
Layout.rightMargin: Style.current.halfPadding
|
Layout.rightMargin: Style.current.halfPadding
|
||||||
visible: isImage
|
visible: isImage
|
||||||
onImageClicked: Global.openImagePopup(chatImage, messageContextMenu)
|
onImageClicked: {
|
||||||
|
Global.openImagePopup(chatImage)
|
||||||
|
}
|
||||||
onImageRemoved: {
|
onImageRemoved: {
|
||||||
if (control.fileUrlsAndSources.length > index && control.fileUrlsAndSources[index]) {
|
if (control.fileUrlsAndSources.length > index && control.fileUrlsAndSources[index]) {
|
||||||
control.fileUrlsAndSources.splice(index, 1)
|
control.fileUrlsAndSources.splice(index, 1)
|
||||||
|
|
|
@ -6,13 +6,12 @@ import QtGraphicalEffects 1.13
|
||||||
|
|
||||||
import utils 1.0
|
import utils 1.0
|
||||||
import shared 1.0
|
import shared 1.0
|
||||||
|
import shared.views.chat 1.0
|
||||||
|
|
||||||
Popup {
|
Popup {
|
||||||
id: root
|
id: root
|
||||||
|
|
||||||
signal clicked(var mouse)
|
property var store
|
||||||
property string imageSource: messageImage.source
|
|
||||||
property var contextMenu
|
|
||||||
|
|
||||||
modal: true
|
modal: true
|
||||||
Overlay.modal: Rectangle {
|
Overlay.modal: Rectangle {
|
||||||
|
@ -32,7 +31,6 @@ Popup {
|
||||||
const maxHeight = Global.applicationWindow.height - 80
|
const maxHeight = Global.applicationWindow.height - 80
|
||||||
const maxWidth = Global.applicationWindow.width - 80
|
const maxWidth = Global.applicationWindow.width - 80
|
||||||
|
|
||||||
|
|
||||||
if (image.sourceSize.width >= maxWidth || image.sourceSize.height >= maxHeight) {
|
if (image.sourceSize.width >= maxWidth || image.sourceSize.height >= maxHeight) {
|
||||||
this.width = maxWidth
|
this.width = maxWidth
|
||||||
this.height = maxHeight
|
this.height = maxHeight
|
||||||
|
@ -44,7 +42,7 @@ Popup {
|
||||||
|
|
||||||
function openPopup(image) {
|
function openPopup(image) {
|
||||||
setPopupData(image);
|
setPopupData(image);
|
||||||
root.open();
|
open()
|
||||||
}
|
}
|
||||||
|
|
||||||
contentItem: AnimatedImage {
|
contentItem: AnimatedImage {
|
||||||
|
@ -61,7 +59,22 @@ Popup {
|
||||||
anchors.fill: parent
|
anchors.fill: parent
|
||||||
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
acceptedButtons: Qt.LeftButton | Qt.RightButton
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.clicked(mouse)
|
if (mouse.button === Qt.LeftButton)
|
||||||
|
root.close()
|
||||||
|
if (mouse.button === Qt.RightButton)
|
||||||
|
Global.openMenu(imageContextMenu,
|
||||||
|
messageImage,
|
||||||
|
{ imageSource: messageImage.source })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: imageContextMenu
|
||||||
|
|
||||||
|
ImageContextMenu {
|
||||||
|
onClosed: {
|
||||||
|
destroy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
import StatusQ.Popups 0.1
|
||||||
|
|
||||||
|
import utils 1.0
|
||||||
|
|
||||||
|
StatusMenu {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property string imageSource
|
||||||
|
|
||||||
|
StatusAction {
|
||||||
|
text: root.imageSource.endsWith(".gif") ? qsTr("Copy GIF")
|
||||||
|
: qsTr("Copy image")
|
||||||
|
icon.name: "copy"
|
||||||
|
enabled: !!root.imageSource
|
||||||
|
onTriggered: {
|
||||||
|
Utils.copyImageToClipboardByUrl(root.imageSource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusAction {
|
||||||
|
text: root.imageSource.endsWith(".gif") ? qsTr("Download GIF")
|
||||||
|
: qsTr("Download image")
|
||||||
|
icon.name: "download"
|
||||||
|
enabled: !!root.imageSource
|
||||||
|
onTriggered: {
|
||||||
|
Global.openDownloadImageDialog(root.imageSource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,7 +25,7 @@ Column {
|
||||||
readonly property alias unfurledImagesCount: d.unfurledImagesCount
|
readonly property alias unfurledImagesCount: d.unfurledImagesCount
|
||||||
property bool isCurrentUser: false
|
property bool isCurrentUser: false
|
||||||
|
|
||||||
signal imageClicked(var image)
|
signal imageClicked(var image, var mouse, var imageSource)
|
||||||
signal linksLoaded()
|
signal linksLoaded()
|
||||||
|
|
||||||
spacing: 4
|
spacing: 4
|
||||||
|
@ -138,7 +138,12 @@ Column {
|
||||||
isOnline: root.store.mainModuleInst.isOnline
|
isOnline: root.store.mainModuleInst.isOnline
|
||||||
asynchronous: true
|
asynchronous: true
|
||||||
isAnimated: result.contentType ? result.contentType.toLowerCase().endsWith("gif") : false
|
isAnimated: result.contentType ? result.contentType.toLowerCase().endsWith("gif") : false
|
||||||
onClicked: isAnimated && !playing ? localAnimationEnabled = true : root.imageClicked(linkImage.imageAlias)
|
onClicked: {
|
||||||
|
if (isAnimated && !playing)
|
||||||
|
localAnimationEnabled = true
|
||||||
|
else
|
||||||
|
root.imageClicked(linkImage.imageAlias, mouse, source)
|
||||||
|
}
|
||||||
imageAlias.cache: localAnimationEnabled // GIFs can only loop/play properly with cache enabled
|
imageAlias.cache: localAnimationEnabled // GIFs can only loop/play properly with cache enabled
|
||||||
Loader {
|
Loader {
|
||||||
width: 45
|
width: 45
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import StatusQ.Popups 0.1
|
||||||
|
|
||||||
|
import shared.controls.chat 1.0
|
||||||
|
|
||||||
|
StatusMenu {
|
||||||
|
id: root
|
||||||
|
|
||||||
|
property alias reactionsModel: emojiRow.reactionsModel
|
||||||
|
property alias messageReactionsModel: emojiRow.messageReactionsModel
|
||||||
|
|
||||||
|
signal toggleReaction(int emojiId)
|
||||||
|
|
||||||
|
width: emojiRow.width
|
||||||
|
|
||||||
|
MessageReactionsRow {
|
||||||
|
id: emojiRow
|
||||||
|
onToggleReaction: {
|
||||||
|
root.toggleReaction(emojiId)
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,6 @@ import QtQuick 2.12
|
||||||
import QtQuick.Controls 2.3
|
import QtQuick.Controls 2.3
|
||||||
import QtQuick.Layouts 1.3
|
import QtQuick.Layouts 1.3
|
||||||
import QtQml.Models 2.3
|
import QtQml.Models 2.3
|
||||||
import QtQuick.Dialogs 1.0
|
|
||||||
|
|
||||||
import StatusQ.Popups 0.1
|
import StatusQ.Popups 0.1
|
||||||
import StatusQ.Components 0.1
|
import StatusQ.Components 0.1
|
||||||
|
@ -20,330 +19,56 @@ StatusMenu {
|
||||||
|
|
||||||
property var store
|
property var store
|
||||||
property var reactionModel
|
property var reactionModel
|
||||||
property alias emojiContainer: emojiContainer
|
|
||||||
|
|
||||||
property string myPublicKey: ""
|
property string myPublicKey: ""
|
||||||
property bool amIChatAdmin: false
|
property bool amIChatAdmin: false
|
||||||
property bool disabledForChat: false
|
property bool disabledForChat: false
|
||||||
|
|
||||||
property string selectedUserPublicKey: ""
|
|
||||||
property string selectedUserDisplayName: ""
|
|
||||||
property string selectedUserIcon: ""
|
|
||||||
|
|
||||||
property int chatType: Constants.chatType.unknown
|
property int chatType: Constants.chatType.unknown
|
||||||
property string messageId: ""
|
property string messageId: ""
|
||||||
property string unparsedText: ""
|
property string unparsedText: ""
|
||||||
property string messageSenderId: ""
|
property string messageSenderId: ""
|
||||||
property int messageContentType: Constants.messageContentType.unknownContentType
|
property int messageContentType: Constants.messageContentType.unknownContentType
|
||||||
property string imageSource: ""
|
|
||||||
|
|
||||||
property bool isProfile: false
|
|
||||||
property bool isRightClickOnImage: false
|
|
||||||
property bool pinnedPopup: false
|
|
||||||
property bool pinMessageAllowedForMembers: false
|
property bool pinMessageAllowedForMembers: false
|
||||||
property bool isDebugEnabled: store && store.isDebugEnabled
|
property bool isDebugEnabled: store && store.isDebugEnabled
|
||||||
property bool isEmoji: false
|
property bool editRestricted: false
|
||||||
property bool isSticker: false
|
|
||||||
property bool hideEmojiPicker: true
|
|
||||||
property bool pinnedMessage: false
|
property bool pinnedMessage: false
|
||||||
property bool canPin: false
|
property bool canPin: false
|
||||||
|
|
||||||
readonly property bool isMyMessage: {
|
readonly property bool isMyMessage: {
|
||||||
return root.messageSenderId !== "" && root.messageSenderId === root.myPublicKey;
|
return root.messageSenderId !== "" && root.messageSenderId === root.myPublicKey;
|
||||||
}
|
}
|
||||||
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);
|
|
||||||
}
|
|
||||||
readonly property bool isContact: {
|
|
||||||
return root.selectedUserPublicKey !== "" && !!contactDetails.isContact
|
|
||||||
}
|
|
||||||
readonly property bool isBlockedContact: (!!contactDetails && contactDetails.isBlocked) || false
|
|
||||||
|
|
||||||
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 !== "" &&
|
|
||||||
root.store.contactsStore.hasPendingContactRequest(root.selectedUserPublicKey);
|
|
||||||
}
|
|
||||||
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
|
|
||||||
|
|
||||||
property var emojiReactionsReactedByUser: []
|
|
||||||
|
|
||||||
signal openProfileClicked(string publicKey)
|
|
||||||
signal pinMessage(string messageId)
|
signal pinMessage(string messageId)
|
||||||
signal unpinMessage(string messageId)
|
signal unpinMessage(string messageId)
|
||||||
signal pinnedMessagesLimitReached(string messageId)
|
signal pinnedMessagesLimitReached(string messageId)
|
||||||
signal jumpToMessage(string messageId)
|
signal showReplyArea(string messageId, string messageSenderId)
|
||||||
signal shouldCloseParentPopup()
|
|
||||||
signal createOneToOneChat(string communityId, string chatId, string ensName)
|
|
||||||
signal showReplyArea()
|
|
||||||
signal toggleReaction(string messageId, int emojiId)
|
signal toggleReaction(string messageId, int emojiId)
|
||||||
signal deleteMessage(string messageId)
|
signal deleteMessage(string messageId)
|
||||||
signal editClicked(string messageId)
|
signal editClicked(string messageId)
|
||||||
|
|
||||||
function show(userNameParam, fromAuthorParam, identiconParam, textParam, nicknameParam, emojiReactionsModel) {
|
width: Math.max(emojiContainer.visible ? emojiContainer.width : 0, 230)
|
||||||
let newEmojiReactions = []
|
|
||||||
if (!!emojiReactionsModel) {
|
|
||||||
emojiReactionsModel.forEach(function (emojiReaction) {
|
|
||||||
newEmojiReactions[emojiReaction.emojiId] = emojiReaction.currentUserReacted
|
|
||||||
})
|
|
||||||
}
|
|
||||||
root.emojiReactionsReactedByUser = newEmojiReactions;
|
|
||||||
|
|
||||||
popup()
|
|
||||||
}
|
|
||||||
|
|
||||||
onClosed: {
|
|
||||||
// Reset selectedUserPublicKey so that associated properties get recalculated on re-open
|
|
||||||
selectedUserPublicKey = ""
|
|
||||||
}
|
|
||||||
|
|
||||||
width: Math.max(emojiContainer.visible ? emojiContainer.width : 0,
|
|
||||||
(root.isRightClickOnImage && !root.pinnedPopup) ? 176 : 230)
|
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
id: emojiContainer
|
id: emojiContainer
|
||||||
width: emojiRow.width
|
width: emojiRow.width
|
||||||
height: visible ? emojiRow.height : 0
|
height: visible ? emojiRow.height : 0
|
||||||
visible: !root.hideEmojiPicker && (root.isEmoji || !root.isProfile) && !root.pinnedPopup && !root.disabledForChat
|
visible: !root.disabledForChat
|
||||||
|
|
||||||
Row {
|
MessageReactionsRow {
|
||||||
id: emojiRow
|
id: emojiRow
|
||||||
spacing: Style.current.halfPadding
|
reactionsModel: root.reactionModel
|
||||||
leftPadding: Style.current.halfPadding
|
bottomPadding: Style.current.padding
|
||||||
rightPadding: Style.current.halfPadding
|
onToggleReaction: {
|
||||||
bottomPadding: root.isEmoji ? 0 : Style.current.padding
|
|
||||||
|
|
||||||
Repeater {
|
|
||||||
model: root.reactionModel
|
|
||||||
delegate: EmojiReaction {
|
|
||||||
source: Style.svg(filename)
|
|
||||||
emojiId: model.emojiId
|
|
||||||
reactedByUser: !!root.emojiReactionsReactedByUser[model.emojiId]
|
|
||||||
onCloseModal: {
|
|
||||||
root.toggleReaction(root.messageId, emojiId)
|
root.toggleReaction(root.messageId, emojiId)
|
||||||
root.close()
|
close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ProfileHeader {
|
|
||||||
visible: root.isProfile
|
|
||||||
width: parent.width
|
|
||||||
height: visible ? implicitHeight : 0
|
|
||||||
|
|
||||||
displayNameVisible: false
|
|
||||||
displayNamePlusIconsVisible: true
|
|
||||||
editButtonVisible: false
|
|
||||||
displayName: root.selectedUserDisplayName
|
|
||||||
pubkey: root.selectedUserPublicKey
|
|
||||||
icon: root.selectedUserIcon
|
|
||||||
trustStatus: contactDetails && contactDetails.trustStatus ? contactDetails.trustStatus
|
|
||||||
: Constants.trustStatus.unknown
|
|
||||||
isContact: root.isContact
|
|
||||||
isCurrentUser: root.isMe
|
|
||||||
userIsEnsVerified: (!!contactDetails && contactDetails.ensVerified) || false
|
|
||||||
}
|
|
||||||
|
|
||||||
Item {
|
|
||||||
visible: root.isProfile
|
|
||||||
height: visible ? root.topPadding : 0
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusMenuSeparator {
|
StatusMenuSeparator {
|
||||||
anchors.bottom: viewProfileAction.top
|
visible: emojiContainer.visible
|
||||||
visible: !root.isEmoji && !root.hideEmojiPicker && !pinnedPopup
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusAction {
|
|
||||||
id: copyImageAction
|
|
||||||
text: (root.imageSource.endsWith(".gif")) ? qsTr("Copy GIF") : qsTr("Copy image")
|
|
||||||
onTriggered: {
|
|
||||||
if (root.imageSource) {
|
|
||||||
root.store.copyImageToClipboardByUrl(root.imageSource)
|
|
||||||
}
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
icon.name: "copy"
|
|
||||||
enabled: root.isRightClickOnImage && !root.pinnedPopup
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusAction {
|
|
||||||
id: downloadImageAction
|
|
||||||
text: (root.imageSource.endsWith(".gif")) ? qsTr("Download GIF") : qsTr("Download image")
|
|
||||||
onTriggered: {
|
|
||||||
fileDialog.open()
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
icon.name: "download"
|
|
||||||
enabled: root.isRightClickOnImage && !root.pinnedPopup
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewProfileMenuItem {
|
|
||||||
id: viewProfileAction
|
|
||||||
enabled: root.isProfile && !root.pinnedPopup
|
|
||||||
onTriggered: {
|
|
||||||
root.openProfileClicked(root.selectedUserPublicKey)
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SendMessageMenuItem {
|
|
||||||
id: sendMessageMenuItem
|
|
||||||
enabled: root.isProfile && root.isContact && !root.isBlockedContact
|
|
||||||
onTriggered: {
|
|
||||||
root.createOneToOneChat("", root.selectedUserPublicKey, "")
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SendContactRequestMenuItem {
|
|
||||||
id: sendContactRequestMenuItem
|
|
||||||
enabled: root.isProfile && !root.isMe && !root.isContact
|
|
||||||
&& !root.isBlockedContact && !root.hasPendingContactRequest
|
|
||||||
onTriggered: {
|
|
||||||
Global.openContactRequestPopup(root.selectedUserPublicKey, null)
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusAction {
|
|
||||||
id: verifyIdentityAction
|
|
||||||
text: qsTr("Verify Identity")
|
|
||||||
icon.name: "checkmark-circle"
|
|
||||||
enabled: root.isProfile && !root.isMe && root.isContact
|
|
||||||
&& !root.isBlockedContact
|
|
||||||
&& root.outgoingVerificationStatus === Constants.verificationStatus.unverified
|
|
||||||
&& !root.hasActiveReceivedVerificationRequestFrom
|
|
||||||
onTriggered: {
|
|
||||||
Global.openSendIDRequestPopup(root.selectedUserPublicKey, null)
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusAction {
|
|
||||||
id: pendingIdentityAction
|
|
||||||
text: isVerificationRequestSent ||
|
|
||||||
root.incomingVerificationStatus === Constants.verificationStatus.verified ?
|
|
||||||
qsTr("ID Request Pending....") :
|
|
||||||
qsTr("Respond to ID Request...")
|
|
||||||
icon.name: "checkmark-circle"
|
|
||||||
enabled: root.isProfile && !root.isMe && root.isContact
|
|
||||||
&& !root.isBlockedContact && !root.isTrusted
|
|
||||||
&& (root.hasActiveReceivedVerificationRequestFrom
|
|
||||||
|| root.isVerificationRequestSent)
|
|
||||||
onTriggered: {
|
|
||||||
if (hasActiveReceivedVerificationRequestFrom) {
|
|
||||||
Global.openIncomingIDRequestPopup(root.selectedUserPublicKey, null)
|
|
||||||
} else if (root.isVerificationRequestSent) {
|
|
||||||
Global.openOutgoingIDRequestPopup(root.selectedUserPublicKey, null)
|
|
||||||
}
|
|
||||||
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusAction {
|
|
||||||
id: renameAction
|
|
||||||
text: qsTr("Rename")
|
|
||||||
icon.name: "edit_pencil"
|
|
||||||
enabled: root.isProfile && !root.isMe
|
|
||||||
onTriggered: {
|
|
||||||
Global.openNicknamePopupRequested(root.selectedUserPublicKey, contactDetails.localNickname,
|
|
||||||
"%1 (%2)".arg(root.selectedUserDisplayName).arg(Utils.getElidedCompressedPk(root.selectedUserPublicKey)))
|
|
||||||
root.close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusAction {
|
|
||||||
id: unblockAction
|
|
||||||
text: qsTr("Unblock User")
|
|
||||||
icon.name: "remove-circle"
|
|
||||||
enabled: root.isProfile && !root.isMe && root.isBlockedContact
|
|
||||||
onTriggered: Global.unblockContactRequested(root.selectedUserPublicKey, root.selectedUserDisplayName)
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusMenuSeparator {
|
|
||||||
visible: blockMenuItem.enabled || markUntrustworthyMenuItem.enabled || removeUntrustworthyMarkMenuItem.enabled
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusAction {
|
|
||||||
id: markUntrustworthyMenuItem
|
|
||||||
text: qsTr("Mark as Untrustworthy")
|
|
||||||
icon.name: "warning"
|
|
||||||
type: StatusAction.Type.Danger
|
|
||||||
enabled: root.isProfile && !root.isMe && root.userTrustIsUnknown
|
|
||||||
onTriggered: root.store.contactsStore.markUntrustworthy(root.selectedUserPublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusAction {
|
|
||||||
id: removeUntrustworthyMarkMenuItem
|
|
||||||
text: qsTr("Remove Untrustworthy Mark")
|
|
||||||
icon.name: "warning"
|
|
||||||
enabled: root.isProfile && !root.isMe && root.userIsUntrustworthy
|
|
||||||
onTriggered: root.store.contactsStore.removeTrustStatus(root.selectedUserPublicKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusAction {
|
|
||||||
text: qsTr("Remove Contact")
|
|
||||||
icon.name: "remove-contact"
|
|
||||||
type: StatusAction.Type.Danger
|
|
||||||
enabled: root.isContact && !root.isBlockedContact && !root.hasPendingContactRequest
|
|
||||||
onTriggered: {
|
|
||||||
Global.removeContactRequested(root.selectedUserDisplayName, root.selectedUserPublicKey);
|
|
||||||
root.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
StatusAction {
|
|
||||||
id: blockMenuItem
|
|
||||||
text: qsTr("Block User")
|
|
||||||
icon.name: "cancel"
|
|
||||||
type: StatusAction.Type.Danger
|
|
||||||
enabled: root.isProfile && !root.isMe && !root.isBlockedContact
|
|
||||||
onTriggered: Global.blockContactRequested(root.selectedUserPublicKey, root.selectedUserDisplayName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusAction {
|
StatusAction {
|
||||||
|
@ -351,15 +76,10 @@ StatusMenu {
|
||||||
text: qsTr("Reply to")
|
text: qsTr("Reply to")
|
||||||
icon.name: "chat"
|
icon.name: "chat"
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.showReplyArea()
|
root.showReplyArea(root.messageId, root.messageSenderId)
|
||||||
root.close()
|
root.close()
|
||||||
}
|
}
|
||||||
enabled: (!root.hideEmojiPicker &&
|
enabled: !root.disabledForChat
|
||||||
!root.isEmoji &&
|
|
||||||
!root.isProfile &&
|
|
||||||
!root.pinnedPopup &&
|
|
||||||
!root.isRightClickOnImage &&
|
|
||||||
!root.disabledForChat)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
StatusAction {
|
StatusAction {
|
||||||
|
@ -370,12 +90,7 @@ StatusMenu {
|
||||||
}
|
}
|
||||||
icon.name: "edit"
|
icon.name: "edit"
|
||||||
enabled: root.isMyMessage &&
|
enabled: root.isMyMessage &&
|
||||||
!root.hideEmojiPicker &&
|
!root.editRestricted &&
|
||||||
!root.isEmoji &&
|
|
||||||
!root.isSticker &&
|
|
||||||
!root.isProfile &&
|
|
||||||
!root.pinnedPopup &&
|
|
||||||
!root.isRightClickOnImage &&
|
|
||||||
!root.disabledForChat
|
!root.disabledForChat
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,13 +118,8 @@ StatusMenu {
|
||||||
|
|
||||||
StatusAction {
|
StatusAction {
|
||||||
id: pinAction
|
id: pinAction
|
||||||
text: {
|
text: root.pinnedMessage ? qsTr("Unpin") : qsTr("Pin")
|
||||||
if (root.pinnedMessage) {
|
icon.name: root.pinnedMessage ? "unpin" : "pin"
|
||||||
return qsTr("Unpin")
|
|
||||||
}
|
|
||||||
return qsTr("Pin")
|
|
||||||
|
|
||||||
}
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
if (root.pinnedMessage) {
|
if (root.pinnedMessage) {
|
||||||
root.unpinMessage(root.messageId)
|
root.unpinMessage(root.messageId)
|
||||||
|
@ -424,14 +134,10 @@ StatusMenu {
|
||||||
root.pinMessage(root.messageId)
|
root.pinMessage(root.messageId)
|
||||||
root.close()
|
root.close()
|
||||||
}
|
}
|
||||||
icon.name: "pin"
|
|
||||||
enabled: {
|
enabled: {
|
||||||
if (root.isProfile || root.isEmoji || root.isRightClickOnImage || root.disabledForChat)
|
if (root.disabledForChat)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
if (root.pinnedPopup)
|
|
||||||
return true
|
|
||||||
|
|
||||||
switch (root.chatType) {
|
switch (root.chatType) {
|
||||||
case Constants.chatType.profile:
|
case Constants.chatType.profile:
|
||||||
return false
|
return false
|
||||||
|
@ -449,10 +155,9 @@ StatusMenu {
|
||||||
|
|
||||||
StatusMenuSeparator {
|
StatusMenuSeparator {
|
||||||
visible: deleteMessageAction.enabled &&
|
visible: deleteMessageAction.enabled &&
|
||||||
(viewProfileAction.enabled ||
|
(replyToMenuItem.enabled ||
|
||||||
sendMessageMenuItem.enabled ||
|
|
||||||
replyToMenuItem.enabled ||
|
|
||||||
copyMessageMenuItem.enabled ||
|
copyMessageMenuItem.enabled ||
|
||||||
|
copyMessageIdAction ||
|
||||||
editMessageAction.enabled ||
|
editMessageAction.enabled ||
|
||||||
pinAction.enabled)
|
pinAction.enabled)
|
||||||
}
|
}
|
||||||
|
@ -461,72 +166,16 @@ StatusMenu {
|
||||||
id: deleteMessageAction
|
id: deleteMessageAction
|
||||||
enabled: (root.isMyMessage || root.amIChatAdmin) &&
|
enabled: (root.isMyMessage || root.amIChatAdmin) &&
|
||||||
!root.disabledForChat &&
|
!root.disabledForChat &&
|
||||||
!root.isProfile &&
|
|
||||||
!root.isEmoji &&
|
|
||||||
!root.pinnedPopup &&
|
|
||||||
!root.isRightClickOnImage &&
|
|
||||||
(root.messageContentType === Constants.messageContentType.messageType ||
|
(root.messageContentType === Constants.messageContentType.messageType ||
|
||||||
root.messageContentType === Constants.messageContentType.stickerType ||
|
root.messageContentType === Constants.messageContentType.stickerType ||
|
||||||
root.messageContentType === Constants.messageContentType.emojiType ||
|
root.messageContentType === Constants.messageContentType.emojiType ||
|
||||||
root.messageContentType === Constants.messageContentType.imageType ||
|
root.messageContentType === Constants.messageContentType.imageType ||
|
||||||
root.messageContentType === Constants.messageContentType.audioType)
|
root.messageContentType === Constants.messageContentType.audioType)
|
||||||
text: qsTr("Delete message")
|
text: qsTr("Delete message")
|
||||||
onTriggered: {
|
|
||||||
if (!localAccountSensitiveSettings.showDeleteMessageWarning) {
|
|
||||||
deleteMessage(messageId)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Global.openPopup(deleteMessageConfirmationDialogComponent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
icon.name: "delete"
|
icon.name: "delete"
|
||||||
type: StatusAction.Type.Danger
|
type: StatusAction.Type.Danger
|
||||||
}
|
|
||||||
|
|
||||||
StatusAction {
|
|
||||||
id: jumpToAction
|
|
||||||
enabled: root.pinnedPopup && !root.isProfile
|
|
||||||
text: qsTr("Jump to")
|
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
root.jumpToMessage(root.messageId)
|
|
||||||
root.close()
|
|
||||||
root.shouldCloseParentPopup()
|
|
||||||
}
|
|
||||||
icon.name: "arrow-up"
|
|
||||||
}
|
|
||||||
|
|
||||||
FileDialog {
|
|
||||||
id: fileDialog
|
|
||||||
title: qsTr("Please choose a directory")
|
|
||||||
selectFolder: true
|
|
||||||
selectExisting: true
|
|
||||||
selectMultiple: false
|
|
||||||
modality: Qt.NonModal
|
|
||||||
onAccepted: {
|
|
||||||
if (root.imageSource) {
|
|
||||||
root.store.downloadImageByUrl(root.imageSource, fileDialog.fileUrl)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
|
||||||
id: deleteMessageConfirmationDialogComponent
|
|
||||||
ConfirmationDialog {
|
|
||||||
header.title: qsTr("Confirm deleting this message")
|
|
||||||
confirmationText: qsTr("Are you sure you want to delete this message? Be aware that other clients are not guaranteed to delete the message as well.")
|
|
||||||
height: 260
|
|
||||||
checkbox.visible: true
|
|
||||||
executeConfirm: function () {
|
|
||||||
if (checkbox.checked) {
|
|
||||||
localAccountSensitiveSettings.showDeleteMessageWarning = false
|
|
||||||
}
|
|
||||||
|
|
||||||
close()
|
|
||||||
root.deleteMessage(messageId)
|
root.deleteMessage(messageId)
|
||||||
}
|
}
|
||||||
onClosed: {
|
|
||||||
destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,8 @@ Loader {
|
||||||
property var messageStore
|
property var messageStore
|
||||||
property var usersStore
|
property var usersStore
|
||||||
property var contactsStore
|
property var contactsStore
|
||||||
property var messageContextMenu: null
|
property var chatContentModule
|
||||||
|
|
||||||
property string channelEmoji
|
property string channelEmoji
|
||||||
property bool isActiveChannel: false
|
property bool isActiveChannel: false
|
||||||
|
|
||||||
|
@ -34,6 +35,7 @@ Loader {
|
||||||
// without an explicit need to fetch those details via message store/module.
|
// without an explicit need to fetch those details via message store/module.
|
||||||
property bool isChatBlocked: false
|
property bool isChatBlocked: false
|
||||||
|
|
||||||
|
property string chatId
|
||||||
property string messageId: ""
|
property string messageId: ""
|
||||||
property string communityId: ""
|
property string communityId: ""
|
||||||
|
|
||||||
|
@ -120,67 +122,59 @@ Loader {
|
||||||
readonly property bool isExpired: d.getIsExpired(messageTimestamp, messageOutgoingStatus)
|
readonly property bool isExpired: d.getIsExpired(messageTimestamp, messageOutgoingStatus)
|
||||||
readonly property bool isSending: messageOutgoingStatus === Constants.sending && !isExpired
|
readonly property bool isSending: messageOutgoingStatus === Constants.sending && !isExpired
|
||||||
|
|
||||||
signal imageClicked(var image)
|
function openProfileContextMenu(sender, mouse, isReply = false) {
|
||||||
|
if (isReply && !quotedMessageFrom) {
|
||||||
// WARNING: To much arguments here. Create an object argument.
|
// The responseTo message was deleted
|
||||||
property var messageClickHandler: function(sender, point,
|
// so we don't enable to right click the unavailable profile
|
||||||
isProfileClick,
|
|
||||||
isSticker = false,
|
|
||||||
isImage = false,
|
|
||||||
image = null,
|
|
||||||
isEmoji = false,
|
|
||||||
hideEmojiPicker = false,
|
|
||||||
isReply = false,
|
|
||||||
isRightClickOnImage = false,
|
|
||||||
imageSource = "") {
|
|
||||||
|
|
||||||
if (placeholderMessage || !(root.rootStore.mainModuleInst.activeSection.joined || isProfileClick)) {
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
messageContextMenu.myPublicKey = userProfile.pubKey
|
const params = {
|
||||||
messageContextMenu.amIChatAdmin = root.amIChatAdmin
|
selectedUserPublicKey: isReply ? quotedMessageFrom : root.senderId,
|
||||||
messageContextMenu.pinMessageAllowedForMembers = messageStore.isPinMessageAllowedForMembers
|
selectedUserDisplayName: isReply ? quotedMessageAuthorDetailsDisplayName : root.senderDisplayName,
|
||||||
messageContextMenu.chatType = messageStore.chatType
|
selectedUserIcon: isReply ? quotedMessageAuthorDetailsThumbnailImage : root.senderIcon,
|
||||||
|
|
||||||
messageContextMenu.messageId = root.messageId
|
|
||||||
messageContextMenu.unparsedText = root.unparsedText
|
|
||||||
messageContextMenu.messageSenderId = root.senderId
|
|
||||||
messageContextMenu.messageContentType = root.messageContentType
|
|
||||||
messageContextMenu.pinnedMessage = root.pinnedMessage
|
|
||||||
messageContextMenu.canPin = !!root.messageStore && root.messageStore.getNumberOfPinnedMessages() < Constants.maxNumberOfPins
|
|
||||||
|
|
||||||
messageContextMenu.selectedUserPublicKey = root.senderId
|
|
||||||
messageContextMenu.selectedUserDisplayName = root.senderDisplayName
|
|
||||||
messageContextMenu.selectedUserIcon = root.senderIcon
|
|
||||||
|
|
||||||
messageContextMenu.imageSource = imageSource
|
|
||||||
|
|
||||||
messageContextMenu.isProfile = !!isProfileClick
|
|
||||||
messageContextMenu.isRightClickOnImage = isRightClickOnImage
|
|
||||||
messageContextMenu.isEmoji = isEmoji
|
|
||||||
messageContextMenu.isSticker = isSticker
|
|
||||||
messageContextMenu.hideEmojiPicker = hideEmojiPicker
|
|
||||||
|
|
||||||
if (isReply) {
|
|
||||||
if (!quotedMessageFrom) {
|
|
||||||
// The responseTo message was deleted so we don't eneble to right click the unaviable profile
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
messageContextMenu.messageSenderId = quotedMessageFrom
|
|
||||||
messageContextMenu.selectedUserPublicKey = quotedMessageFrom
|
|
||||||
messageContextMenu.selectedUserDisplayName = quotedMessageAuthorDetailsDisplayName
|
|
||||||
messageContextMenu.selectedUserIcon = quotedMessageAuthorDetailsThumbnailImage
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emoji container is not a menu item of messageContextMenu so checking it separatly
|
Global.openMenu(profileContextMenuComponent, sender, params)
|
||||||
if (messageContextMenu.checkIfEmpty() && !isEmoji) {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
messageContextMenu.parent = sender
|
function openMessageContextMenu() {
|
||||||
messageContextMenu.popup(point)
|
if (placeholderMessage || !root.rootStore.mainModuleInst.activeSection.joined)
|
||||||
return true
|
return
|
||||||
|
|
||||||
|
const params = {
|
||||||
|
myPublicKey: userProfile.pubKey,
|
||||||
|
amIChatAdmin: root.amIChatAdmin,
|
||||||
|
pinMessageAllowedForMembers: messageStore.isPinMessageAllowedForMembers,
|
||||||
|
chatType: messageStore.chatType,
|
||||||
|
|
||||||
|
messageId: root.messageId,
|
||||||
|
unparsedText: root.unparsedText,
|
||||||
|
messageSenderId: root.senderId,
|
||||||
|
messageContentType: root.messageContentType,
|
||||||
|
pinnedMessage: root.pinnedMessage,
|
||||||
|
canPin: !!root.messageStore && root.messageStore.getNumberOfPinnedMessages() < Constants.maxNumberOfPins,
|
||||||
|
editRestricted: root.isSticker || root.isImage,
|
||||||
|
}
|
||||||
|
|
||||||
|
Global.openMenu(messageContextMenuComponent, this, params)
|
||||||
|
}
|
||||||
|
|
||||||
|
function setMessageActive(messageId, active) {
|
||||||
|
|
||||||
|
// TODO: Is argument messageId actually needed?
|
||||||
|
// It was probably used with dynamic scoping,
|
||||||
|
// but not this method can be moved to private `d`.
|
||||||
|
// Probably that it was done this way, because `MessageView` is reused as delegate.
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
d.activeMessage = messageId;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (d.activeMessage === messageId) {
|
||||||
|
d.activeMessage = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
signal showReplyArea(string messageId, string author)
|
signal showReplyArea(string messageId, string author)
|
||||||
|
@ -237,22 +231,7 @@ Loader {
|
||||||
property string activeMessage
|
property string activeMessage
|
||||||
readonly property bool isMessageActive: d.activeMessage === root.messageId
|
readonly property bool isMessageActive: d.activeMessage === root.messageId
|
||||||
|
|
||||||
function setMessageActive(messageId, active) {
|
readonly property bool addReactionAllowed: !root.isInPinnedPopup && !root.isChatBlocked
|
||||||
|
|
||||||
// TODO: Is argument messageId actually needed?
|
|
||||||
// It was probably used with dynamic scoping,
|
|
||||||
// but not this method can be moved to private `d`.
|
|
||||||
// Probably that it was done this way, because `MessageView` is reused as delegate.
|
|
||||||
|
|
||||||
if (active) {
|
|
||||||
d.activeMessage = messageId;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (d.activeMessage === messageId) {
|
|
||||||
d.activeMessage = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function nextMessageHasHeader() {
|
function nextMessageHasHeader() {
|
||||||
if(!root.nextMessageAsJsonObj) {
|
if(!root.nextMessageAsJsonObj) {
|
||||||
|
@ -303,15 +282,24 @@ Loader {
|
||||||
return StatusMessage.ContentType.Unknown;
|
return StatusMessage.ContentType.Unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addReactionClicked(mouseArea, mouse) {
|
||||||
|
if (!d.addReactionAllowed)
|
||||||
|
return
|
||||||
|
// Don't use mouseArea as parent, as it will be destroyed right after opening menu
|
||||||
|
const point = mouseArea.mapToItem(root, mouse.x, mouse.y)
|
||||||
|
Global.openMenu(addReactionContextMenu, root, {}, point)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onImageClicked(image, mouse, imageSource) {
|
||||||
|
switch (mouse.button) {
|
||||||
Connections {
|
case Qt.LeftButton:
|
||||||
enabled: d.isMessageActive
|
Global.openImagePopup(image)
|
||||||
target: root.messageContextMenu
|
break;
|
||||||
function onClosed() {
|
case Qt.RightButton:
|
||||||
d.setMessageActive(root.messageId, false)
|
Global.openMenu(imageContextMenuComponent, image, { imageSource })
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,7 +486,6 @@ Loader {
|
||||||
disableHover: root.disableHover ||
|
disableHover: root.disableHover ||
|
||||||
delegate.hideQuickActions ||
|
delegate.hideQuickActions ||
|
||||||
(root.chatLogView && root.chatLogView.moving) ||
|
(root.chatLogView && root.chatLogView.moving) ||
|
||||||
(root.messageContextMenu && root.messageContextMenu.opened) ||
|
|
||||||
Global.popupOpened
|
Global.popupOpened
|
||||||
|
|
||||||
disableEmojis: root.isChatBlocked
|
disableEmojis: root.isChatBlocked
|
||||||
|
@ -514,15 +501,8 @@ Loader {
|
||||||
|
|
||||||
onEditCompleted: delegate.editCompletedHandler(newMsgText)
|
onEditCompleted: delegate.editCompletedHandler(newMsgText)
|
||||||
|
|
||||||
onImageClicked: {
|
onImageClicked: (image, mouse, imageSource) => {
|
||||||
switch (mouse.button) {
|
d.onImageClicked(image, mouse, imageSource)
|
||||||
case Qt.LeftButton:
|
|
||||||
root.imageClicked(image, mouse);
|
|
||||||
break;
|
|
||||||
case Qt.RightButton:
|
|
||||||
root.messageClickHandler(image, Qt.point(mouse.x, mouse.y), false, false, true, image, false, true, false, true, imageSource)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onLinkActivated: {
|
onLinkActivated: {
|
||||||
|
@ -542,13 +522,11 @@ Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
onProfilePictureClicked: {
|
onProfilePictureClicked: {
|
||||||
if (root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true))
|
root.openProfileContextMenu(sender, mouse)
|
||||||
d.setMessageActive(root.messageId, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onReplyProfileClicked: {
|
onReplyProfileClicked: {
|
||||||
if (root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true, false, false, null, false, false, true))
|
root.openProfileContextMenu(sender, mouse, true)
|
||||||
d.setMessageActive(root.messageId, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onReplyMessageClicked: {
|
onReplyMessageClicked: {
|
||||||
|
@ -557,8 +535,7 @@ Loader {
|
||||||
}
|
}
|
||||||
|
|
||||||
onSenderNameClicked: {
|
onSenderNameClicked: {
|
||||||
if (root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true))
|
root.openProfileContextMenu(sender, mouse)
|
||||||
d.setMessageActive(root.messageId, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onToggleReactionClicked: {
|
onToggleReactionClicked: {
|
||||||
|
@ -573,12 +550,8 @@ Loader {
|
||||||
root.messageStore.toggleReaction(root.messageId, emojiId)
|
root.messageStore.toggleReaction(root.messageId, emojiId)
|
||||||
}
|
}
|
||||||
|
|
||||||
onAddReactionClicked: {
|
onAddReactionClicked: (sender, mouse) => {
|
||||||
if (root.isChatBlocked)
|
d.addReactionClicked(sender, mouse)
|
||||||
return
|
|
||||||
|
|
||||||
if (root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), false, false, false, null, true, false))
|
|
||||||
d.setMessageActive(root.messageId, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onStickerClicked: {
|
onStickerClicked: {
|
||||||
|
@ -592,12 +565,9 @@ Loader {
|
||||||
mouseArea {
|
mouseArea {
|
||||||
acceptedButtons: Qt.RightButton
|
acceptedButtons: Qt.RightButton
|
||||||
enabled: !root.isChatBlocked &&
|
enabled: !root.isChatBlocked &&
|
||||||
!root.placeholderMessage &&
|
!root.placeholderMessage
|
||||||
delegate.contentType !== StatusMessage.ContentType.Image
|
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (root.messageClickHandler(this, Qt.point(mouse.x, mouse.y),
|
root.openMessageContextMenu()
|
||||||
false, false, false, null, root.isEmoji, false, false, false, ""))
|
|
||||||
d.setMessageActive(root.messageId, true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -714,7 +684,6 @@ Loader {
|
||||||
usersStore: root.usersStore
|
usersStore: root.usersStore
|
||||||
emojiPopup: root.emojiPopup
|
emojiPopup: root.emojiPopup
|
||||||
stickersPopup: root.stickersPopup
|
stickersPopup: root.stickersPopup
|
||||||
messageContextMenu: root.messageContextMenu
|
|
||||||
|
|
||||||
chatType: root.messageStore.chatType
|
chatType: root.messageStore.chatType
|
||||||
isEdit: true
|
isEdit: true
|
||||||
|
@ -736,8 +705,8 @@ Loader {
|
||||||
messageStore: root.messageStore
|
messageStore: root.messageStore
|
||||||
store: root.rootStore
|
store: root.rootStore
|
||||||
isCurrentUser: root.amISender
|
isCurrentUser: root.amISender
|
||||||
onImageClicked: {
|
onImageClicked: (image, mouse, imageSource) => {
|
||||||
root.imageClicked(image);
|
d.onImageClicked(image, mouse, imageSource)
|
||||||
}
|
}
|
||||||
onLinksLoaded: {
|
onLinksLoaded: {
|
||||||
// If there is only one image and no links, hide the message
|
// If there is only one image and no links, hide the message
|
||||||
|
@ -765,7 +734,7 @@ Loader {
|
||||||
|
|
||||||
quickActions: [
|
quickActions: [
|
||||||
Loader {
|
Loader {
|
||||||
active: !root.isInPinnedPopup && delegate.hovered && !delegate.hideQuickActions
|
active: d.addReactionAllowed && delegate.hovered && !delegate.hideQuickActions
|
||||||
visible: active
|
visible: active
|
||||||
sourceComponent: StatusFlatRoundButton {
|
sourceComponent: StatusFlatRoundButton {
|
||||||
width: d.chatButtonSize
|
width: d.chatButtonSize
|
||||||
|
@ -773,9 +742,8 @@ Loader {
|
||||||
icon.name: "reaction-b"
|
icon.name: "reaction-b"
|
||||||
type: StatusFlatRoundButton.Type.Tertiary
|
type: StatusFlatRoundButton.Type.Tertiary
|
||||||
tooltip.text: qsTr("Add reaction")
|
tooltip.text: qsTr("Add reaction")
|
||||||
onClicked: {
|
onClicked: (mouse) => {
|
||||||
if (root.messageClickHandler(delegate, mapToItem(delegate, mouse.x, mouse.y), false, false, false, null, true, false))
|
d.addReactionClicked(this, mouse)
|
||||||
d.setMessageActive(root.messageId, true)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -791,9 +759,6 @@ Loader {
|
||||||
tooltip.text: qsTr("Reply")
|
tooltip.text: qsTr("Reply")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
root.showReplyArea(root.messageId, root.senderId)
|
root.showReplyArea(root.messageId, root.senderId)
|
||||||
if (messageContextMenu.closeParentPopup) {
|
|
||||||
messageContextMenu.closeParentPopup()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -886,12 +851,7 @@ Loader {
|
||||||
type: StatusFlatRoundButton.Type.Tertiary
|
type: StatusFlatRoundButton.Type.Tertiary
|
||||||
tooltip.text: qsTr("Delete")
|
tooltip.text: qsTr("Delete")
|
||||||
onClicked: {
|
onClicked: {
|
||||||
if (!localAccountSensitiveSettings.showDeleteMessageWarning) {
|
messageStore.warnAndDeleteMessage(root.messageId)
|
||||||
messageStore.deleteMessage(root.messageId)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Global.openPopup(deleteMessageConfirmationDialogComponent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -900,29 +860,6 @@ Loader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Component {
|
|
||||||
id: deleteMessageConfirmationDialogComponent
|
|
||||||
|
|
||||||
ConfirmationDialog {
|
|
||||||
confirmButtonObjectName: "chatButtonsPanelConfirmDeleteMessageButton"
|
|
||||||
header.title: qsTr("Confirm deleting this message")
|
|
||||||
confirmationText: qsTr("Are you sure you want to delete this message? Be aware that other clients are not guaranteed to delete the message as well.")
|
|
||||||
height: 260
|
|
||||||
checkbox.visible: true
|
|
||||||
executeConfirm: function () {
|
|
||||||
if (checkbox.checked) {
|
|
||||||
localAccountSensitiveSettings.showDeleteMessageWarning = false
|
|
||||||
}
|
|
||||||
|
|
||||||
close()
|
|
||||||
messageStore.deleteMessage(root.messageId)
|
|
||||||
}
|
|
||||||
onClosed: {
|
|
||||||
destroy()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Component {
|
Component {
|
||||||
id: newMessagesMarkerComponent
|
id: newMessagesMarkerComponent
|
||||||
|
|
||||||
|
@ -931,4 +868,110 @@ Loader {
|
||||||
timestamp: root.messageTimestamp
|
timestamp: root.messageTimestamp
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: addReactionContextMenu
|
||||||
|
|
||||||
|
MessageAddReactionContextMenu {
|
||||||
|
reactionsModel: root.rootStore.emojiReactionsModel
|
||||||
|
onToggleReaction: (emojiId) => {
|
||||||
|
root.messageStore.toggleReaction(root.messageId, emojiId)
|
||||||
|
}
|
||||||
|
onOpened: {
|
||||||
|
root.setMessageActive(root.messageId, true)
|
||||||
|
}
|
||||||
|
onClosed: {
|
||||||
|
root.setMessageActive(root.messageId, false)
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: imageContextMenuComponent
|
||||||
|
|
||||||
|
ImageContextMenu {
|
||||||
|
onClosed: {
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: profileContextMenuComponent
|
||||||
|
|
||||||
|
ProfileContextMenu {
|
||||||
|
store: root.rootStore
|
||||||
|
onOpenProfileClicked: (publicKey) => {
|
||||||
|
Global.openProfilePopup(publicKey, null)
|
||||||
|
}
|
||||||
|
onCreateOneToOneChat: (communityId, chatId, ensName) => {
|
||||||
|
Global.changeAppSectionBySectionType(Constants.appSection.chat)
|
||||||
|
root.rootStore.chatCommunitySectionModule.createOneToOneChat("", chatId, ensName)
|
||||||
|
}
|
||||||
|
onOpened: {
|
||||||
|
root.setMessageActive(root.messageId, true)
|
||||||
|
}
|
||||||
|
onClosed: {
|
||||||
|
root.setMessageActive(root.messageId, false)
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: messageContextMenuComponent
|
||||||
|
|
||||||
|
MessageContextMenuView {
|
||||||
|
store: root.rootStore
|
||||||
|
reactionModel: root.rootStore.emojiReactionsModel
|
||||||
|
disabledForChat: !root.rootStore.isUserAllowedToSendMessage
|
||||||
|
|
||||||
|
onPinMessage: (messageId) => {
|
||||||
|
root.messageStore.pinMessage(messageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
onUnpinMessage: (messageId) => {
|
||||||
|
root.messageStore.unpinMessage(messageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
onPinnedMessagesLimitReached: (messageId) => {
|
||||||
|
if (!root.chatContentModule) {
|
||||||
|
console.warn("error on open pinned messages limit reached from message context menu - chat content module is not set")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Global.openPinnedMessagesPopupRequested(root.rootStore,
|
||||||
|
root.messageStore,
|
||||||
|
root.chatContentModule.pinnedMessagesModel,
|
||||||
|
messageId,
|
||||||
|
root.chatId)
|
||||||
|
}
|
||||||
|
|
||||||
|
onToggleReaction: (messageId, emojiId) => {
|
||||||
|
root.messageStore.toggleReaction(messageId, emojiId)
|
||||||
|
}
|
||||||
|
|
||||||
|
onDeleteMessage: (messageId) => {
|
||||||
|
root.messageStore.warnAndDeleteMessage(messageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
onEditClicked: (messageId) => {
|
||||||
|
root.messageStore.setEditModeOn(messageId)
|
||||||
|
}
|
||||||
|
|
||||||
|
onShowReplyArea: (messageId, senderId) => {
|
||||||
|
root.showReplyArea(messageId, senderId)
|
||||||
|
}
|
||||||
|
|
||||||
|
onOpened: {
|
||||||
|
root.setMessageActive(model.id, true)
|
||||||
|
}
|
||||||
|
onClosed: {
|
||||||
|
root.setMessageActive(model.id, false)
|
||||||
|
destroy()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,241 @@
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.3
|
||||||
|
import QtQuick.Layouts 1.3
|
||||||
|
import QtQml.Models 2.3
|
||||||
|
|
||||||
|
import StatusQ.Popups 0.1
|
||||||
|
import StatusQ.Components 0.1
|
||||||
|
|
||||||
|
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 var store
|
||||||
|
|
||||||
|
property string myPublicKey: ""
|
||||||
|
|
||||||
|
property string selectedUserPublicKey: ""
|
||||||
|
property string selectedUserDisplayName: ""
|
||||||
|
property string selectedUserIcon: ""
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
readonly property bool isContact: {
|
||||||
|
return root.selectedUserPublicKey !== "" && !!contactDetails.isContact
|
||||||
|
}
|
||||||
|
readonly property bool isBlockedContact: (!!contactDetails && contactDetails.isBlocked) || false
|
||||||
|
|
||||||
|
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 !== "" &&
|
||||||
|
root.store.contactsStore.hasPendingContactRequest(root.selectedUserPublicKey);
|
||||||
|
}
|
||||||
|
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
|
||||||
|
|
||||||
|
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 = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
width: 230
|
||||||
|
|
||||||
|
ProfileHeader {
|
||||||
|
width: parent.width
|
||||||
|
height: visible ? implicitHeight : 0
|
||||||
|
|
||||||
|
displayNameVisible: false
|
||||||
|
displayNamePlusIconsVisible: true
|
||||||
|
editButtonVisible: false
|
||||||
|
displayName: root.selectedUserDisplayName
|
||||||
|
pubkey: root.selectedUserPublicKey
|
||||||
|
icon: root.selectedUserIcon
|
||||||
|
trustStatus: contactDetails && contactDetails.trustStatus ? contactDetails.trustStatus
|
||||||
|
: Constants.trustStatus.unknown
|
||||||
|
isContact: root.isContact
|
||||||
|
isCurrentUser: root.isMe
|
||||||
|
userIsEnsVerified: (!!contactDetails && contactDetails.ensVerified) || false
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusMenuSeparator {
|
||||||
|
topPadding: root.topPadding
|
||||||
|
}
|
||||||
|
|
||||||
|
ViewProfileMenuItem {
|
||||||
|
id: viewProfileAction
|
||||||
|
onTriggered: {
|
||||||
|
root.openProfileClicked(root.selectedUserPublicKey)
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SendMessageMenuItem {
|
||||||
|
id: sendMessageMenuItem
|
||||||
|
enabled: root.isContact && !root.isBlockedContact
|
||||||
|
onTriggered: {
|
||||||
|
root.createOneToOneChat("", root.selectedUserPublicKey, "")
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SendContactRequestMenuItem {
|
||||||
|
id: sendContactRequestMenuItem
|
||||||
|
enabled: !root.isMe && !root.isContact
|
||||||
|
&& !root.isBlockedContact && !root.hasPendingContactRequest
|
||||||
|
onTriggered: {
|
||||||
|
Global.openContactRequestPopup(root.selectedUserPublicKey, null)
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusAction {
|
||||||
|
id: verifyIdentityAction
|
||||||
|
text: qsTr("Verify Identity")
|
||||||
|
icon.name: "checkmark-circle"
|
||||||
|
enabled: !root.isMe && root.isContact
|
||||||
|
&& !root.isBlockedContact
|
||||||
|
&& root.outgoingVerificationStatus === Constants.verificationStatus.unverified
|
||||||
|
&& !root.hasActiveReceivedVerificationRequestFrom
|
||||||
|
onTriggered: {
|
||||||
|
Global.openSendIDRequestPopup(root.selectedUserPublicKey, null)
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusAction {
|
||||||
|
id: pendingIdentityAction
|
||||||
|
text: isVerificationRequestSent ||
|
||||||
|
root.incomingVerificationStatus === Constants.verificationStatus.verified ?
|
||||||
|
qsTr("ID Request Pending....") :
|
||||||
|
qsTr("Respond to ID Request...")
|
||||||
|
icon.name: "checkmark-circle"
|
||||||
|
enabled: !root.isMe && root.isContact
|
||||||
|
&& !root.isBlockedContact && !root.isTrusted
|
||||||
|
&& (root.hasActiveReceivedVerificationRequestFrom
|
||||||
|
|| root.isVerificationRequestSent)
|
||||||
|
onTriggered: {
|
||||||
|
if (hasActiveReceivedVerificationRequestFrom) {
|
||||||
|
Global.openIncomingIDRequestPopup(root.selectedUserPublicKey, null)
|
||||||
|
} else if (root.isVerificationRequestSent) {
|
||||||
|
Global.openOutgoingIDRequestPopup(root.selectedUserPublicKey, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusAction {
|
||||||
|
id: renameAction
|
||||||
|
text: qsTr("Rename")
|
||||||
|
icon.name: "edit_pencil"
|
||||||
|
enabled: !root.isMe
|
||||||
|
onTriggered: {
|
||||||
|
Global.openNicknamePopupRequested(root.selectedUserPublicKey, contactDetails.localNickname,
|
||||||
|
"%1 (%2)".arg(root.selectedUserDisplayName).arg(Utils.getElidedCompressedPk(root.selectedUserPublicKey)))
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusAction {
|
||||||
|
id: unblockAction
|
||||||
|
text: qsTr("Unblock User")
|
||||||
|
icon.name: "remove-circle"
|
||||||
|
enabled: !root.isMe && root.isBlockedContact
|
||||||
|
onTriggered: Global.unblockContactRequested(root.selectedUserPublicKey, root.selectedUserDisplayName)
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusMenuSeparator {
|
||||||
|
visible: blockMenuItem.enabled
|
||||||
|
|| markUntrustworthyMenuItem.enabled
|
||||||
|
|| removeUntrustworthyMarkMenuItem.enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusAction {
|
||||||
|
id: markUntrustworthyMenuItem
|
||||||
|
text: qsTr("Mark as Untrustworthy")
|
||||||
|
icon.name: "warning"
|
||||||
|
type: StatusAction.Type.Danger
|
||||||
|
enabled: !root.isMe && root.userTrustIsUnknown
|
||||||
|
onTriggered: root.store.contactsStore.markUntrustworthy(root.selectedUserPublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusAction {
|
||||||
|
id: removeUntrustworthyMarkMenuItem
|
||||||
|
text: qsTr("Remove Untrustworthy Mark")
|
||||||
|
icon.name: "warning"
|
||||||
|
enabled: !root.isMe && root.userIsUntrustworthy
|
||||||
|
onTriggered: root.store.contactsStore.removeTrustStatus(root.selectedUserPublicKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusAction {
|
||||||
|
text: qsTr("Remove Contact")
|
||||||
|
icon.name: "remove-contact"
|
||||||
|
type: StatusAction.Type.Danger
|
||||||
|
enabled: root.isContact && !root.isBlockedContact && !root.hasPendingContactRequest
|
||||||
|
onTriggered: {
|
||||||
|
Global.removeContactRequested(root.selectedUserDisplayName, root.selectedUserPublicKey)
|
||||||
|
root.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StatusAction {
|
||||||
|
id: blockMenuItem
|
||||||
|
text: qsTr("Block User")
|
||||||
|
icon.name: "cancel"
|
||||||
|
type: StatusAction.Type.Danger
|
||||||
|
enabled: !root.isMe && !root.isBlockedContact
|
||||||
|
onTriggered: Global.blockContactRequested(root.selectedUserPublicKey, root.selectedUserDisplayName)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -10,3 +10,6 @@ ProfileHeaderContextMenuView 1.0 ProfileHeaderContextMenuView.qml
|
||||||
TransactionBubbleView 1.0 TransactionBubbleView.qml
|
TransactionBubbleView 1.0 TransactionBubbleView.qml
|
||||||
NewMessagesMarker 1.0 NewMessagesMarker.qml
|
NewMessagesMarker 1.0 NewMessagesMarker.qml
|
||||||
SimplifiedMessageView 1.0 SimplifiedMessageView.qml
|
SimplifiedMessageView 1.0 SimplifiedMessageView.qml
|
||||||
|
ImageContextMenu 1.0 ImageContextMenu.qml
|
||||||
|
ProfileContextMenu 1.0 ProfileContextMenu.qml
|
||||||
|
MessageAddReactionContextMenu 1.0 MessageAddReactionContextMenu.qml
|
||||||
|
|
|
@ -32,7 +32,7 @@ QtObject {
|
||||||
signal openDownloadModalRequested(bool available, string version, string url)
|
signal openDownloadModalRequested(bool available, string version, string url)
|
||||||
signal openChangeProfilePicPopup(var cb)
|
signal openChangeProfilePicPopup(var cb)
|
||||||
signal openBackUpSeedPopup()
|
signal openBackUpSeedPopup()
|
||||||
signal openImagePopup(var image, var contextMenu)
|
signal openImagePopup(var image)
|
||||||
signal openProfilePopupRequested(string publicKey, var parentPopup)
|
signal openProfilePopupRequested(string publicKey, var parentPopup)
|
||||||
signal openEditDisplayNamePopup()
|
signal openEditDisplayNamePopup()
|
||||||
signal openActivityCenterPopupRequested()
|
signal openActivityCenterPopupRequested()
|
||||||
|
@ -42,6 +42,8 @@ QtObject {
|
||||||
signal openInviteFriendsToCommunityPopup(var community, var communitySectionModule, var cb)
|
signal openInviteFriendsToCommunityPopup(var community, var communitySectionModule, var cb)
|
||||||
signal openIncomingIDRequestPopup(string publicKey, var cb)
|
signal openIncomingIDRequestPopup(string publicKey, var cb)
|
||||||
signal openOutgoingIDRequestPopup(string publicKey, var cb)
|
signal openOutgoingIDRequestPopup(string publicKey, var cb)
|
||||||
|
signal openDeleteMessagePopup(string messageId, var messageStore)
|
||||||
|
signal openDownloadImageDialog(string imageSource)
|
||||||
signal contactRenamed(string publicKey)
|
signal contactRenamed(string publicKey)
|
||||||
|
|
||||||
signal openLink(string link)
|
signal openLink(string link)
|
||||||
|
@ -77,4 +79,13 @@ QtObject {
|
||||||
function changeAppSectionBySectionType(sectionType, subsection = 0) {
|
function changeAppSectionBySectionType(sectionType, subsection = 0) {
|
||||||
root.appSectionBySectionTypeChanged(sectionType, subsection);
|
root.appSectionBySectionTypeChanged(sectionType, subsection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openMenu(menuComponent, menuParent, params = {}, point = undefined) {
|
||||||
|
const menu = menuComponent.createObject(menuParent, params)
|
||||||
|
if (point)
|
||||||
|
menu.popup(point)
|
||||||
|
else
|
||||||
|
menu.popup()
|
||||||
|
return menu
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -678,6 +678,14 @@ QtObject {
|
||||||
return text
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function copyImageToClipboardByUrl(content) {
|
||||||
|
globalUtilsInst.copyImageToClipboardByUrl(content)
|
||||||
|
}
|
||||||
|
|
||||||
|
function downloadImageByUrl(url, path) {
|
||||||
|
globalUtilsInst.downloadImageByUrl(url, path)
|
||||||
|
}
|
||||||
|
|
||||||
// Leave this function at the bottom of the file as QT Creator messes up the code color after this
|
// Leave this function at the bottom of the file as QT Creator messes up the code color after this
|
||||||
function isPunct(c) {
|
function isPunct(c) {
|
||||||
return /(!|\@|#|\$|%|\^|&|\*|\(|\)|\+|\||-|=|\\|{|}|[|]|"|;|'|<|>|\?|,|\.|\/)/.test(c)
|
return /(!|\@|#|\$|%|\^|&|\*|\(|\)|\+|\||-|=|\\|{|}|[|]|"|;|'|<|>|\?|,|\.|\/)/.test(c)
|
||||||
|
|
Loading…
Reference in New Issue