status-desktop/ui/imports/shared/views/chat/MessageContextMenuView.qml

392 lines
12 KiB
QML

import QtQuick 2.12
import QtQuick.Controls 2.3
import QtQuick.Layouts 1.3
import QtQml.Models 2.3
import QtQuick.Dialogs 1.0
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
StatusPopupMenu {
id: root
width: emojiContainer.visible ? emojiContainer.width : 176
property var store
property var reactionModel
property alias emojiContainer: emojiContainer
property string myPublicKey: ""
property bool amIChatAdmin: false
property bool isMyMessage: {
return root.messageSenderId !== "" && root.messageSenderId == root.myPublicKey
}
property int chatType: Constants.chatType.publicChat
property string messageId: ""
property string messageSenderId: ""
property int messageContentType: Constants.messageContentType.unknownContentType
property string selectedUserPublicKey: ""
property string selectedUserDisplayName: ""
property string selectedUserIcon: ""
property bool isSelectedUserIconIdenticon: true
property string imageSource: ""
property bool isProfile: false
property bool isRightClickOnImage: false
property bool pinnedPopup: false
property bool isDebugEnabled: false
property bool emojiOnly: false
property bool hideEmojiPicker: true
property bool pinnedMessage: false
property bool canPin: false
property var setXPosition: function() {return 0}
property var setYPosition: function() {return 0}
property var emojiReactionsReactedByUser: []
signal openProfileClicked(string publicKey)
signal pinMessage(string messageId)
signal unpinMessage(string messageId)
signal pinnedMessagesLimitReached(string messageId)
signal jumpToMessage(string messageId)
signal shouldCloseParentPopup()
signal createOneToOneChat(string chatId, string ensName)
signal showReplyArea()
signal toggleReaction(string messageId, int emojiId)
signal deleteMessage(string messageId)
signal editClicked(string messageId)
onHeightChanged: {
root.y = setYPosition()
}
onWidthChanged: {
root.x = setXPosition()
}
function show(userNameParam, fromAuthorParam, identiconParam, textParam, nicknameParam, emojiReactionsModel) {
let newEmojiReactions = []
if (!!emojiReactionsModel) {
emojiReactionsModel.forEach(function (emojiReaction) {
newEmojiReactions[emojiReaction.emojiId] = emojiReaction.currentUserReacted
})
}
root.emojiReactionsReactedByUser = newEmojiReactions;
/* // copy link feature not ready yet
const numLinkUrls = root.linkUrls.split(" ").length
copyLinkMenu.enabled = numLinkUrls > 1
copyLinkAction.enabled = !!root.linkUrls && numLinkUrls === 1 && !emojiOnly && !root.isProfile
*/
popup()
}
Item {
id: emojiContainer
width: emojiRow.width
height: visible ? emojiRow.height : 0
visible: !root.hideEmojiPicker && (root.emojiOnly || !root.isProfile) && !root.pinnedPopup
Row {
id: emojiRow
spacing: Style.current.halfPadding
leftPadding: Style.current.halfPadding
rightPadding: Style.current.halfPadding
bottomPadding: root.emojiOnly ? 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.close()
}
}
}
}
}
Item {
id: profileHeader
visible: root.isProfile
width: parent.width
height: visible ? profileImage.height + username.height + Style.current.padding : 0
Rectangle {
anchors.fill: parent
visible: mouseArea.containsMouse
color: Style.current.backgroundHover
}
StatusSmartIdenticon {
id: profileImage
anchors.top: parent.top
anchors.topMargin: 4
anchors.horizontalCenter: parent.horizontalCenter
image.source: root.selectedUserIcon
image.isIdenticon: root.isSelectedUserIconIdenticon
}
StyledText {
id: username
text: selectedUserDisplayName
elide: Text.ElideRight
maximumLineCount: 3
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
anchors.top: profileImage.bottom
anchors.topMargin: 4
anchors.left: parent.left
anchors.leftMargin: Style.current.smallPadding
anchors.right: parent.right
anchors.rightMargin: Style.current.smallPadding
font.weight: Font.Medium
font.pixelSize: 15
}
MouseArea {
id: mouseArea
anchors.fill: parent
hoverEnabled: true
cursorShape: Qt.PointingHandCursor
onClicked: {
root.openProfileClicked(root.selectedUserPublicKey)
root.close()
}
}
}
Separator {
anchors.bottom: viewProfileAction.top
visible: !root.emojiOnly && !root.hideEmojiPicker && !pinnedPopup
}
StatusMenuItem {
id: copyImageAction
text: qsTr("Copy image")
onTriggered: {
if (root.imageSource) {
root.store.copyImageToClipboard(root.imageSource)
}
root.close()
}
icon.name: "copy"
enabled: root.isRightClickOnImage && !root.pinnedPopup
}
StatusMenuItem {
id: downloadImageAction
text: qsTr("Download image")
onTriggered: {
fileDialog.open()
root.close()
}
icon.name: "download"
enabled: root.isRightClickOnImage && !root.pinnedPopup
}
StatusMenuItem {
id: viewProfileAction
//% "View Profile"
text: qsTrId("view-profile")
onTriggered: {
root.openProfileClicked(root.selectedUserPublicKey)
root.close()
}
icon.name: "profile"
enabled: root.isProfile && !root.pinnedPopup
}
StatusMenuItem {
id: sendMessageOrReplyTo
text: root.isProfile ?
//% "Send message"
qsTrId("send-message") :
//% "Reply to"
qsTrId("reply-to")
onTriggered: {
if (root.isProfile) {
root.createOneToOneChat(root.selectedUserPublicKey, "")
} else {
root.showReplyArea()
}
root.close()
}
icon.name: "chat"
enabled: root.isProfile && !root.isMyMessage ||
(!root.hideEmojiPicker &&
!root.emojiOnly &&
!root.isProfile &&
!root.pinnedPopup &&
!root.isRightClickOnImage)
}
StatusMenuItem {
id: editMessageAction
//% "Edit message"
text: qsTrId("edit-message")
onTriggered: {
editClicked(messageId)
}
icon.name: "edit"
enabled: root.isMyMessage &&
!root.hideEmojiPicker &&
!root.emojiOnly &&
!root.isProfile &&
!root.pinnedPopup &&
!root.isRightClickOnImage
}
StatusMenuItem {
id: copyMessageIdAction
text: qsTr("Copy Message Id")
icon.name: "chat"
enabled: root.isDebugEnabled && !pinnedPopup
onTriggered: {
root.store.copyToClipboard(SelectedMessage.messageId)
close()
}
}
StatusMenuItem {
id: pinAction
text: {
if (root.pinnedMessage) {
//% "Unpin"
return qsTrId("unpin")
}
//% "Pin"
return qsTrId("pin")
}
onTriggered: {
if (root.pinnedMessage) {
root.unpinMessage(root.messageId)
return
}
if (!root.canPin) {
root.pinnedMessagesLimitReached(root.messageId)
return
}
root.pinMessage(root.messageId)
root.close()
}
icon.name: "pin"
enabled: {
if(root.isProfile || root.emojiOnly || root.isRightClickOnImage)
return false
switch (root.chatType) {
case Constants.chatType.publicChat:
return false
case Constants.chatType.profile:
return false
case Constants.chatType.oneToOne:
return true
case Constants.chatType.privateGroupChat:
return root.amIChatAdmin
case Constants.chatType.communityChat:
return root.amIChatAdmin
default:
return false
}
}
}
StatusMenuSeparator {
visible: deleteMessageAction.enabled &&
(viewProfileAction.enabled ||
sendMessageOrReplyTo.enabled ||
editMessageAction.enabled ||
pinAction.enabled)
}
StatusMenuItem {
id: deleteMessageAction
enabled: root.isMyMessage &&
!root.isProfile &&
!root.emojiOnly &&
!root.pinnedPopup &&
!root.isRightClickOnImage &&
(root.messageContentType === Constants.messageContentType.messageType ||
root.messageContentType === Constants.messageContentType.stickerType ||
root.messageContentType === Constants.messageContentType.emojiType ||
root.messageContentType === Constants.messageContentType.imageType ||
root.messageContentType === Constants.messageContentType.audioType)
//% "Delete message"
text: qsTrId("delete-message")
onTriggered: {
if (!localAccountSensitiveSettings.showDeleteMessageWarning) {
deleteMessage(messageId)
}
else {
Global.openPopup(deleteMessageConfirmationDialogComponent)
}
}
icon.name: "delete"
type: StatusMenuItem.Type.Danger
}
StatusMenuItem {
id: jumpToAction
enabled: root.pinnedPopup
text: qsTr("Jump to")
onTriggered: {
root.jumpToMessage(root.messageId)
root.close()
root.shouldCloseParentPopup()
}
icon.name: "up"
}
FileDialog {
id: fileDialog
title: qsTr("Please choose a directory")
selectFolder: true
modality: Qt.NonModal
onAccepted: {
if (root.imageSource) {
root.store.downloadImage(root.imageSource, fileDialog.fileUrls)
}
fileDialog.close()
}
onRejected: {
fileDialog.close()
}
}
Component {
id: deleteMessageConfirmationDialogComponent
ConfirmationDialog {
//% "Confirm deleting this message"
header.title: qsTrId("confirm-deleting-this-message")
//% "Are you sure you want to delete this message? Be aware that other clients are not guaranteed to delete the message as well."
confirmationText: qsTrId("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)
}
onClosed: {
destroy()
}
}
}
}