diff --git a/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml b/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml index 112336bb5d..850a70a439 100644 --- a/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml +++ b/ui/StatusQ/src/StatusQ/Components/StatusMessage.qml @@ -61,7 +61,6 @@ Rectangle { property double timestamp: 0 property var reactionsModel: [] - readonly property bool dateGroupVisible: dateGroupLabel.visible property bool showHeader: true property bool isActiveMessage: false property bool disableHover: false @@ -71,9 +70,6 @@ Rectangle { property bool profileClickable: true property bool hideMessage: false - property alias previousMessageIndex: dateGroupLabel.previousMessageIndex - property alias previousMessageTimestamp: dateGroupLabel.previousMessageTimestamp - property StatusMessageDetails messageDetails: StatusMessageDetails {} property StatusMessageDetails replyDetails: StatusMessageDetails {} @@ -216,14 +212,6 @@ Rectangle { anchors.topMargin: 8 anchors.bottomMargin: 8 - StatusDateGroupLabel { - id: dateGroupLabel - Layout.fillWidth: true - Layout.topMargin: 20 - messageTimestamp: root.timestamp - visible: text !== "" - } - Loader { Layout.fillWidth: true active: isAReply diff --git a/ui/imports/shared/views/chat/MessageView.qml b/ui/imports/shared/views/chat/MessageView.qml index b653edb545..f944446aed 100644 --- a/ui/imports/shared/views/chat/MessageView.qml +++ b/ui/imports/shared/views/chat/MessageView.qml @@ -1,4 +1,5 @@ -import QtQuick 2.13 +import QtQuick 2.14 +import QtQuick.Layouts 1.14 import utils 1.0 import shared.panels 1.0 @@ -364,476 +365,492 @@ Loader { } } + Component { id: messageComponent - StatusMessage { - id: delegate + ColumnLayout { + spacing: 0 - function convertContentType(value) { - switch (value) { - case Constants.messageContentType.messageType: - return StatusMessage.ContentType.Text; - case Constants.messageContentType.stickerType: - return StatusMessage.ContentType.Sticker; - case Constants.messageContentType.emojiType: - return StatusMessage.ContentType.Emoji; - case Constants.messageContentType.transactionType: - return StatusMessage.ContentType.Transaction; - case Constants.messageContentType.imageType: - return StatusMessage.ContentType.Image; - case Constants.messageContentType.audioType: - return StatusMessage.ContentType.Audio; - case Constants.messageContentType.communityInviteType: - return StatusMessage.ContentType.Invitation; - case Constants.messageContentType.fetchMoreMessagesButton: - case Constants.messageContentType.chatIdentifier: - case Constants.messageContentType.unknownContentType: - case Constants.messageContentType.statusType: - case Constants.messageContentType.systemMessagePrivateGroupType: - case Constants.messageContentType.gapType: - case Constants.messageContentType.editType: - default: - return StatusMessage.ContentType.Unknown; - } + StatusDateGroupLabel { + id: dateGroupLabel + Layout.fillWidth: true + Layout.topMargin: 16 + Layout.bottomMargin: 16 + messageTimestamp: root.messageTimestamp + previousMessageIndex: root.prevMessageIndex + previousMessageTimestamp: root.prevMsgTimestamp + visible: text !== "" } - readonly property int contentType: convertContentType(root.messageContentType) - readonly property bool isReply: root.responseTo !== "" + StatusMessage { + id: delegate + Layout.fillWidth: true - property var replyMessage: getReplyMessage() - - readonly property string replySenderId: replyMessage ? replyMessage.senderId : "" - - function getReplyMessage() { - return root.messageStore && isReply && root.responseToExistingMessage ? root.messageStore.getMessageByIdAsJson(root.responseTo) : null - } - - function editCompletedHandler(newMessageText) { - const message = root.rootStore.plainText(StatusQUtils.Emoji.deparse(newMessageText)) - if (message.length <= 0) - return; - - const interpretedMessage = root.messageStore.interpretMessage(message) - root.messageStore.setEditModeOff(root.messageId) - root.messageStore.editMessage(root.messageId, interpretedMessage) - } - - audioMessageInfoText: qsTr("Audio Message") - cancelButtonText: qsTr("Cancel") - saveButtonText: qsTr("Save") - loadingImageText: qsTr("Loading image...") - errorLoadingImageText: qsTr("Error loading the image") - resendText: qsTr("Resend") - pinnedMsgInfoText: root.isDiscordMessage ? qsTr("Pinned") : qsTr("Pinned by") - reactionIcons: [ - Style.svg("emojiReactions/heart"), - Style.svg("emojiReactions/thumbsUp"), - Style.svg("emojiReactions/thumbsDown"), - Style.svg("emojiReactions/laughing"), - Style.svg("emojiReactions/sad"), - Style.svg("emojiReactions/angry"), - ] - - timestamp: root.messageTimestamp - editMode: root.editModeOn - isAReply: delegate.isReply - isEdited: root.isEdited - hasMention: root.hasMention - isPinned: root.pinnedMessage - pinnedBy: root.pinnedMessage && !root.isDiscordMessage ? Utils.getContactDetailsAsJson(root.messagePinnedBy).displayName : "" - hasExpired: root.isExpired - reactionsModel: root.reactionsModel - - previousMessageIndex: root.prevMessageIndex - previousMessageTimestamp: root.prevMsgTimestamp - - showHeader: root.authorCurrentMsg !== root.authorPrevMsg || - root.shouldRepeatHeader || dateGroupVisible || isAReply - isActiveMessage: d.isMessageActive - - disableHover: root.disableHover || - (root.chatLogView && root.chatLogView.flickingVertically) || - activityCenterMessage || - root.messageContextMenu.opened || - !!Global.profilePopupOpened || - !!Global.popupOpened - - hideQuickActions: root.isChatBlocked || - root.placeholderMessage || - root.activityCenterMessage || - root.isInPinnedPopup - hideMessage: d.isSingleImage && d.unfurledLinksCount === 1 - - overrideBackground: root.activityCenterMessage || root.placeholderMessage - overrideBackgroundColor: { - if (root.activityCenterMessage && root.activityCenterMessageRead) - return Utils.setColorAlpha(Style.current.blue, 0.1); - return "transparent"; - } - profileClickable: !root.isDiscordMessage - messageAttachments: root.messageAttachments - - timestampString: Utils.formatShortTime(timestamp, - localAccountSensitiveSettings.is24hTimeFormat) - - timestampTooltipString: Utils.formatLongDateTime(timestamp, - localAccountSensitiveSettings.isDDMMYYDateFormat, - localAccountSensitiveSettings.is24hTimeFormat); - - onEditCancelled: { - root.messageStore.setEditModeOff(root.messageId) - } - - onEditCompleted: { - delegate.editCompletedHandler(newMsgText) - } - - onImageClicked: { - switch (mouse.button) { - 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: { - if (link.startsWith('//')) { - const pubkey = link.replace("//", ""); - Global.openProfilePopup(pubkey) - return; - } else if (link.startsWith('#')) { - rootStore.chatCommunitySectionModule.switchToChannel(link.replace("#", "")) - return; + function convertContentType(value) { + switch (value) { + case Constants.messageContentType.messageType: + return StatusMessage.ContentType.Text; + case Constants.messageContentType.stickerType: + return StatusMessage.ContentType.Sticker; + case Constants.messageContentType.emojiType: + return StatusMessage.ContentType.Emoji; + case Constants.messageContentType.transactionType: + return StatusMessage.ContentType.Transaction; + case Constants.messageContentType.imageType: + return StatusMessage.ContentType.Image; + case Constants.messageContentType.audioType: + return StatusMessage.ContentType.Audio; + case Constants.messageContentType.communityInviteType: + return StatusMessage.ContentType.Invitation; + case Constants.messageContentType.fetchMoreMessagesButton: + case Constants.messageContentType.chatIdentifier: + case Constants.messageContentType.unknownContentType: + case Constants.messageContentType.statusType: + case Constants.messageContentType.systemMessagePrivateGroupType: + case Constants.messageContentType.gapType: + case Constants.messageContentType.editType: + default: + return StatusMessage.ContentType.Unknown; + } } - Global.openLink(link) - } + readonly property int contentType: convertContentType(root.messageContentType) + readonly property bool isReply: root.responseTo !== "" + + property var replyMessage: getReplyMessage() - onProfilePictureClicked: { - d.setMessageActive(root.messageId, true); - root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true); - } + readonly property string replySenderId: replyMessage ? replyMessage.senderId : "" - onReplyProfileClicked: { - d.setMessageActive(root.messageId, true); - root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true, false, false, null, false, false, true); - } - - onSenderNameClicked: { - d.setMessageActive(root.messageId, true); - root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true); - } - - onToggleReactionClicked: { - if (root.isChatBlocked) - return - - if (!root.messageStore) { - console.error("Reaction can not be toggled, message store is not valid") - return + function getReplyMessage() { + return root.messageStore && isReply && root.responseToExistingMessage ? root.messageStore.getMessageByIdAsJson(root.responseTo) : null } - root.messageStore.toggleReaction(root.messageId, emojiId) - } + function editCompletedHandler(newMessageText) { + const message = root.rootStore.plainText(StatusQUtils.Emoji.deparse(newMessageText)) + if (message.length <= 0) + return; - onAddReactionClicked: { - if (root.isChatBlocked) - return; + const interpretedMessage = root.messageStore.interpretMessage(message) + root.messageStore.setEditModeOff(root.messageId) + root.messageStore.editMessage(root.messageId, interpretedMessage) + } - d.setMessageActive(root.messageId, true); - root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), false, false, false, null, true, false); - } + audioMessageInfoText: qsTr("Audio Message") + cancelButtonText: qsTr("Cancel") + saveButtonText: qsTr("Save") + loadingImageText: qsTr("Loading image...") + errorLoadingImageText: qsTr("Error loading the image") + resendText: qsTr("Resend") + pinnedMsgInfoText: root.isDiscordMessage ? qsTr("Pinned") : qsTr("Pinned by") + reactionIcons: [ + Style.svg("emojiReactions/heart"), + Style.svg("emojiReactions/thumbsUp"), + Style.svg("emojiReactions/thumbsDown"), + Style.svg("emojiReactions/laughing"), + Style.svg("emojiReactions/sad"), + Style.svg("emojiReactions/angry"), + ] - onStickerClicked: { - root.openStickerPackPopup(root.stickerPack); - } + timestamp: root.messageTimestamp + editMode: root.editModeOn + isAReply: delegate.isReply + isEdited: root.isEdited + hasMention: root.hasMention + isPinned: root.pinnedMessage + pinnedBy: root.pinnedMessage && !root.isDiscordMessage ? Utils.getContactDetailsAsJson(root.messagePinnedBy).displayName : "" + hasExpired: root.isExpired + reactionsModel: root.reactionsModel - mouseArea { - acceptedButtons: root.activityCenterMessage ? Qt.LeftButton : Qt.RightButton - enabled: !root.isChatBlocked && - !root.placeholderMessage && - delegate.contentType !== StatusMessage.ContentType.Image - onClicked: { + showHeader: root.authorCurrentMsg !== root.authorPrevMsg || + root.shouldRepeatHeader || dateGroupLabel.visible || isAReply + isActiveMessage: d.isMessageActive + + disableHover: root.disableHover || + (root.chatLogView && root.chatLogView.flickingVertically) || + activityCenterMessage || + root.messageContextMenu.opened || + !!Global.profilePopupOpened || + !!Global.popupOpened + + hideQuickActions: root.isChatBlocked || + root.placeholderMessage || + root.activityCenterMessage || + root.isInPinnedPopup + hideMessage: d.isSingleImage && d.unfurledLinksCount === 1 + + overrideBackground: root.activityCenterMessage || root.placeholderMessage + overrideBackgroundColor: { + if (root.activityCenterMessage && root.activityCenterMessageRead) + return Utils.setColorAlpha(Style.current.blue, 0.1); + return "transparent"; + } + profileClickable: !root.isDiscordMessage + messageAttachments: root.messageAttachments + + timestampString: Utils.formatShortTime(timestamp, + localAccountSensitiveSettings.is24hTimeFormat) + + timestampTooltipString: Utils.formatLongDateTime(timestamp, + localAccountSensitiveSettings.isDDMMYYDateFormat, + localAccountSensitiveSettings.is24hTimeFormat); + + onEditCancelled: { + root.messageStore.setEditModeOff(root.messageId) + } + + onEditCompleted: { + delegate.editCompletedHandler(newMsgText) + } + + onImageClicked: { + switch (mouse.button) { + 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: { + if (link.startsWith('//')) { + const pubkey = link.replace("//", ""); + Global.openProfilePopup(pubkey) + return; + } + + if (link.startsWith('#')) { + rootStore.chatCommunitySectionModule.switchToChannel(link.replace("#", "")) + return; + } + + Global.openLink(link) + } + + onProfilePictureClicked: { d.setMessageActive(root.messageId, true); - root.messageClickHandler(this, Qt.point(mouse.x, mouse.y), - false, false, false, null, root.isEmoji, false, false, false, ""); + root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true); } - } - messageDetails: StatusMessageDetails { - contentType: delegate.contentType - messageOriginInfo: isDiscordMessage ? qsTr("Imported from discord") : "" - messageText: root.messageText - messageContent: { - switch (delegate.contentType) - { - case StatusMessage.ContentType.Sticker: - return root.sticker; - case StatusMessage.ContentType.Image: - return root.messageImage; + onReplyProfileClicked: { + d.setMessageActive(root.messageId, true); + root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true, false, false, null, false, false, true); + } + + onSenderNameClicked: { + d.setMessageActive(root.messageId, true); + root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true); + } + + onToggleReactionClicked: { + if (root.isChatBlocked) + return + + if (!root.messageStore) { + console.error("Reaction can not be toggled, message store is not valid") + return } - if (root.isDiscordMessage && root.messageImage != "") { - return root.messageImage + + root.messageStore.toggleReaction(root.messageId, emojiId) + } + + onAddReactionClicked: { + if (root.isChatBlocked) + return; + + d.setMessageActive(root.messageId, true); + root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), false, false, false, null, true, false); + } + + onStickerClicked: { + root.openStickerPackPopup(root.stickerPack); + } + + mouseArea { + acceptedButtons: root.activityCenterMessage ? Qt.LeftButton : Qt.RightButton + enabled: !root.isChatBlocked && + !root.placeholderMessage && + delegate.contentType !== StatusMessage.ContentType.Image + onClicked: { + d.setMessageActive(root.messageId, true); + root.messageClickHandler(this, Qt.point(mouse.x, mouse.y), + false, false, false, null, root.isEmoji, false, false, false, ""); } - return ""; } - amISender: root.amISender - sender.id: root.senderIsEnsVerified ? "" : Utils.getCompressedPk(root.senderId) - sender.displayName: root.senderDisplayName - sender.secondaryName: root.senderOptionalName - sender.isEnsVerified: root.senderIsEnsVerified - sender.isContact: root.senderIsAdded - sender.trustIndicator: root.senderTrustStatus - sender.profileImage { - width: 40 - height: 40 - name: root.senderIcon || "" - assetSettings.isImage: root.isDiscordMessage || root.senderIcon.startsWith("data") - pubkey: root.senderId - colorId: Utils.colorIdForPubkey(root.senderId) - colorHash: Utils.getColorHashAsJson(root.senderId, false, !root.isDiscordMessage) - showRing: !root.isDiscordMessage - } - } + messageDetails: StatusMessageDetails { + contentType: delegate.contentType + messageOriginInfo: isDiscordMessage ? qsTr("Imported from discord") : "" + messageText: root.messageText + messageContent: { + switch (delegate.contentType) + { + case StatusMessage.ContentType.Sticker: + return root.sticker; + case StatusMessage.ContentType.Image: + return root.messageImage; + } + if (root.isDiscordMessage && root.messageImage != "") { + return root.messageImage + } + return ""; + } - replyDetails: StatusMessageDetails { - messageText: delegate.replyMessage ? delegate.replyMessage.messageText + amISender: root.amISender + sender.id: root.senderIsEnsVerified ? "" : Utils.getCompressedPk(root.senderId) + sender.displayName: root.senderDisplayName + sender.secondaryName: root.senderOptionalName + sender.isEnsVerified: root.senderIsEnsVerified + sender.isContact: root.senderIsAdded + sender.trustIndicator: root.senderTrustStatus + sender.profileImage { + width: 40 + height: 40 + name: root.senderIcon || "" + assetSettings.isImage: root.isDiscordMessage || root.senderIcon.startsWith("data") + pubkey: root.senderId + colorId: Utils.colorIdForPubkey(root.senderId) + colorHash: Utils.getColorHashAsJson(root.senderId, false, !root.isDiscordMessage) + showRing: !root.isDiscordMessage + } + } + + replyDetails: StatusMessageDetails { + messageText: delegate.replyMessage ? delegate.replyMessage.messageText //: There is should be a message for reply which source message was deleted : qsTr("<deleted>") - contentType: delegate.replyMessage ? delegate.convertContentType(delegate.replyMessage.contentType) : 0 - messageContent: { - if (!delegate.replyMessage) + contentType: delegate.replyMessage ? delegate.convertContentType(delegate.replyMessage.contentType) : 0 + messageContent: { + if (!delegate.replyMessage) + return ""; + switch (contentType) { + case StatusMessage.ContentType.Sticker: + return delegate.replyMessage.sticker; + case StatusMessage.ContentType.Image: + return delegate.replyMessage.messageImage; + } return ""; - switch (contentType) { - case StatusMessage.ContentType.Sticker: - return delegate.replyMessage.sticker; - case StatusMessage.ContentType.Image: - return delegate.replyMessage.messageImage; } - return ""; - } - amISender: delegate.replyMessage && delegate.replyMessage.amISender - sender.id: delegate.replyMessage ? delegate.replyMessage.senderId : "" - sender.isContact: delegate.replyMessage && delegate.replyMessage.senderIsAdded - sender.displayName: delegate.replyMessage ? delegate.replyMessage.senderDisplayName: "" - sender.isEnsVerified: delegate.replyMessage && delegate.replyMessage.senderEnsVerified - sender.secondaryName: delegate.replyMessage ? delegate.replyMessage.senderOptionalName : "" - sender.profileImage { - width: 20 - height: 20 - name: delegate.replyMessage ? delegate.replyMessage.senderIcon : "" - assetSettings.isImage: delegate.replyMessage && (delegate.replyMessage.messageContentType == Constants.discordMessageType || delegate.replyMessage.senderIcon.startsWith("data")) - showRing: delegate.replyMessage && delegate.replyMessage.messageContentType != Constants.discordMessageType - pubkey: delegate.replySenderId - colorId: Utils.colorIdForPubkey(delegate.replySenderId) - colorHash: Utils.getColorHashAsJson(delegate.replySenderId, false, !root.isDiscordMessage) - } - } - - statusChatInput: StatusChatInput { - id: editTextInput - objectName: "editMessageInput" - - readonly property string messageText: editTextInput.textInput.text - - // TODO: Move this property and Escape handler to StatusChatInput - property bool suggestionsOpened: false - - width: parent.width - - Keys.onEscapePressed: { - if (!suggestionsOpened) { - delegate.editCancelled() - } - suggestionsOpened = false - } - - store: root.rootStore - usersStore: root.usersStore - emojiPopup: root.emojiPopup - messageContextMenu: root.messageContextMenu - - chatType: root.messageStore.getChatType() - isEdit: true - - onSendMessage: { - delegate.editCompletedHandler(editTextInput.textInput.text) - } - - suggestions.onVisibleChanged: { - if (suggestions.visible) { - suggestionsOpened = true + amISender: delegate.replyMessage && delegate.replyMessage.amISender + sender.id: delegate.replyMessage ? delegate.replyMessage.senderId : "" + sender.isContact: delegate.replyMessage && delegate.replyMessage.senderIsAdded + sender.displayName: delegate.replyMessage ? delegate.replyMessage.senderDisplayName: "" + sender.isEnsVerified: delegate.replyMessage && delegate.replyMessage.senderEnsVerified + sender.secondaryName: delegate.replyMessage ? delegate.replyMessage.senderOptionalName : "" + sender.profileImage { + width: 20 + height: 20 + name: delegate.replyMessage ? delegate.replyMessage.senderIcon : "" + assetSettings.isImage: delegate.replyMessage && (delegate.replyMessage.messageContentType == Constants.discordMessageType || delegate.replyMessage.senderIcon.startsWith("data")) + showRing: delegate.replyMessage && delegate.replyMessage.messageContentType != Constants.discordMessageType + pubkey: delegate.replySenderId + colorId: Utils.colorIdForPubkey(delegate.replySenderId) + colorHash: Utils.getColorHashAsJson(delegate.replySenderId, false, !root.isDiscordMessage) } } - Component.onCompleted: { - parseMessage(root.messageText); - } - } + statusChatInput: StatusChatInput { + id: editTextInput + objectName: "editMessageInput" + + readonly property string messageText: editTextInput.textInput.text + + // TODO: Move this property and Escape handler to StatusChatInput + property bool suggestionsOpened: false + + width: parent.width + + Keys.onEscapePressed: { + if (!suggestionsOpened) { + delegate.editCancelled() + } + suggestionsOpened = false + } - linksComponent: Component { - LinksMessageView { - linksModel: linkUrlsModel - container: root - messageStore: root.messageStore store: root.rootStore - isCurrentUser: root.amISender - onImageClicked: { - root.imageClicked(image); + usersStore: root.usersStore + emojiPopup: root.emojiPopup + messageContextMenu: root.messageContextMenu + + chatType: root.messageStore.getChatType() + isEdit: true + + onSendMessage: { + delegate.editCompletedHandler(editTextInput.textInput.text) } - Component.onCompleted: d.unfurledLinksCount = Qt.binding(() => unfurledLinksCount) - Component.onDestruction: d.unfurledLinksCount = 0 + suggestions.onVisibleChanged: { + if (suggestions.visible) { + suggestionsOpened = true + } + } + + Component.onCompleted: { + parseMessage(root.messageText); + } } + + linksComponent: Component { + LinksMessageView { + linksModel: linkUrlsModel + container: root + messageStore: root.messageStore + store: root.rootStore + isCurrentUser: root.amISender + onImageClicked: { + root.imageClicked(image); + } + + Component.onCompleted: d.unfurledLinksCount = Qt.binding(() => unfurledLinksCount) + Component.onDestruction: d.unfurledLinksCount = 0 + } + } + + transcationComponent: Component { + TransactionBubbleView { + transactionParams: root.transactionParams + store: root.rootStore + contactsStore: root.contactsStore + } + } + + invitationComponent: Component { + InvitationBubbleView { + store: root.rootStore + communityId: root.communityId + } + } + + quickActions: [ + Loader { + active: !root.isInPinnedPopup + sourceComponent: StatusFlatRoundButton { + width: d.chatButtonSize + height: d.chatButtonSize + icon.name: "reaction-b" + type: StatusFlatRoundButton.Type.Tertiary + tooltip.text: qsTr("Add reaction") + onClicked: { + d.setMessageActive(root.messageId, true) + root.messageClickHandler(this, Qt.point(mouse.x, mouse.y), false, false, false, null, true, false) + } + } + }, + Loader { + active: !root.isInPinnedPopup + sourceComponent: StatusFlatRoundButton { + objectName: "replyToMessageButton" + width: d.chatButtonSize + height: d.chatButtonSize + icon.name: "reply" + type: StatusFlatRoundButton.Type.Tertiary + tooltip.text: qsTr("Reply") + onClicked: { + root.showReplyArea(root.messageId, root.senderId) + if (messageContextMenu.closeParentPopup) { + messageContextMenu.closeParentPopup() + } + } + } + }, + Loader { + active: !root.isInPinnedPopup && root.isText && !root.editModeOn && root.amISender + visible: active + sourceComponent: StatusFlatRoundButton { + objectName: "editMessageButton" + width: d.chatButtonSize + height: d.chatButtonSize + icon.name: "edit_pencil" + type: StatusFlatRoundButton.Type.Tertiary + tooltip.text: qsTr("Edit") + onClicked: { + root.messageStore.setEditModeOn(root.messageId) + } + } + }, + Loader { + active: { + if (!root.messageStore) + return false + + const chatType = root.messageStore.getChatType(); + const amIChatAdmin = root.messageStore.amIChatAdmin(); + const pinMessageAllowedForMembers = root.messageStore.pinMessageAllowedForMembers() + + return chatType === Constants.chatType.oneToOne || + chatType === Constants.chatType.privateGroupChat && amIChatAdmin || + chatType === Constants.chatType.communityChat && (amIChatAdmin || pinMessageAllowedForMembers); + + } + sourceComponent: StatusFlatRoundButton { + objectName: "MessageView_toggleMessagePin" + width: d.chatButtonSize + height: d.chatButtonSize + icon.name: root.pinnedMessage ? "unpin" : "pin" + type: StatusFlatRoundButton.Type.Tertiary + tooltip.text: root.pinnedMessage ? qsTr("Unpin") : qsTr("Pin") + onClicked: { + if (root.pinnedMessage) { + messageStore.unpinMessage(root.messageId) + return; + } + + if (!!root.messageStore && root.messageStore.getNumberOfPinnedMessages() < Constants.maxNumberOfPins) { + messageStore.pinMessage(root.messageId) + return; + } + + if (!chatContentModule) { + console.warn("error on open pinned messages limit reached from message context menu - chat content module is not set") + return; + } + + Global.openPopup(Global.pinnedMessagesPopup, { + store: root.rootStore, + messageStore: messageStore, + pinnedMessagesModel: chatContentModule.pinnedMessagesModel, + messageToPin: root.messageId + }); + } + } + }, + Loader { + active: { + if (root.isInPinnedPopup) + return false; + if (!root.messageStore) + return false; + const isMyMessage = senderId !== "" && senderId === userProfile.pubKey; + const chatType = root.messageStore.getChatType(); + return isMyMessage && + (messageContentType === Constants.messageContentType.messageType || + messageContentType === Constants.messageContentType.stickerType || + messageContentType === Constants.messageContentType.emojiType || + messageContentType === Constants.messageContentType.imageType || + messageContentType === Constants.messageContentType.audioType); + } + sourceComponent: StatusFlatRoundButton { + objectName: "chatDeleteMessageButton" + width: d.chatButtonSize + height: d.chatButtonSize + icon.name: "delete" + type: StatusFlatRoundButton.Type.Tertiary + tooltip.text: qsTr("Delete") + onClicked: { + if (!localAccountSensitiveSettings.showDeleteMessageWarning) { + messageStore.deleteMessage(root.messageId) + } + else { + Global.openPopup(deleteMessageConfirmationDialogComponent) + } + } + } + } + ] } - - transcationComponent: Component { - TransactionBubbleView { - transactionParams: root.transactionParams - store: root.rootStore - contactsStore: root.contactsStore - } - } - - invitationComponent: Component { - InvitationBubbleView { - store: root.rootStore - communityId: root.communityId - } - } - - quickActions: [ - Loader { - active: !root.isInPinnedPopup - sourceComponent: StatusFlatRoundButton { - width: d.chatButtonSize - height: d.chatButtonSize - icon.name: "reaction-b" - type: StatusFlatRoundButton.Type.Tertiary - tooltip.text: qsTr("Add reaction") - onClicked: { - d.setMessageActive(root.messageId, true) - root.messageClickHandler(this, Qt.point(mouse.x, mouse.y), false, false, false, null, true, false) - } - } - }, - Loader { - active: !root.isInPinnedPopup - sourceComponent: StatusFlatRoundButton { - objectName: "replyToMessageButton" - width: d.chatButtonSize - height: d.chatButtonSize - icon.name: "reply" - type: StatusFlatRoundButton.Type.Tertiary - tooltip.text: qsTr("Reply") - onClicked: { - root.showReplyArea(root.messageId, root.senderId) - if (messageContextMenu.closeParentPopup) { - messageContextMenu.closeParentPopup() - } - } - } - }, - Loader { - active: !root.isInPinnedPopup && root.isText && !root.editModeOn && root.amISender - visible: active - sourceComponent: StatusFlatRoundButton { - objectName: "editMessageButton" - width: d.chatButtonSize - height: d.chatButtonSize - icon.name: "edit_pencil" - type: StatusFlatRoundButton.Type.Tertiary - tooltip.text: qsTr("Edit") - onClicked: { - root.messageStore.setEditModeOn(root.messageId) - } - } - }, - Loader { - active: { - if (!root.messageStore) - return false - - const chatType = root.messageStore.getChatType(); - const amIChatAdmin = root.messageStore.amIChatAdmin(); - const pinMessageAllowedForMembers = root.messageStore.pinMessageAllowedForMembers() - - return chatType === Constants.chatType.oneToOne || - chatType === Constants.chatType.privateGroupChat && amIChatAdmin || - chatType === Constants.chatType.communityChat && (amIChatAdmin || pinMessageAllowedForMembers); - - } - sourceComponent: StatusFlatRoundButton { - objectName: "MessageView_toggleMessagePin" - width: d.chatButtonSize - height: d.chatButtonSize - icon.name: root.pinnedMessage ? "unpin" : "pin" - type: StatusFlatRoundButton.Type.Tertiary - tooltip.text: root.pinnedMessage ? qsTr("Unpin") : qsTr("Pin") - onClicked: { - if (root.pinnedMessage) { - messageStore.unpinMessage(root.messageId) - return; - } - - if (!!root.messageStore && root.messageStore.getNumberOfPinnedMessages() < Constants.maxNumberOfPins) { - messageStore.pinMessage(root.messageId) - return; - } - - if (!chatContentModule) { - console.warn("error on open pinned messages limit reached from message context menu - chat content module is not set") - return; - } - - Global.openPopup(Global.pinnedMessagesPopup, { - store: root.rootStore, - messageStore: messageStore, - pinnedMessagesModel: chatContentModule.pinnedMessagesModel, - messageToPin: root.messageId - }); - } - } - }, - Loader { - active: { - if (root.isInPinnedPopup) - return false; - if (!root.messageStore) - return false; - const isMyMessage = senderId !== "" && senderId === userProfile.pubKey; - const chatType = root.messageStore.getChatType(); - return isMyMessage && - (messageContentType === Constants.messageContentType.messageType || - messageContentType === Constants.messageContentType.stickerType || - messageContentType === Constants.messageContentType.emojiType || - messageContentType === Constants.messageContentType.imageType || - messageContentType === Constants.messageContentType.audioType); - } - sourceComponent: StatusFlatRoundButton { - objectName: "chatDeleteMessageButton" - width: d.chatButtonSize - height: d.chatButtonSize - icon.name: "delete" - type: StatusFlatRoundButton.Type.Tertiary - tooltip.text: qsTr("Delete") - onClicked: { - if (!localAccountSensitiveSettings.showDeleteMessageWarning) { - messageStore.deleteMessage(root.messageId) - } - else { - Global.openPopup(deleteMessageConfirmationDialogComponent) - } - } - } - } - ] } }