fix(MessageView): Changed visual borders of message

This commit is contained in:
Igor Sirotin 2022-10-07 16:50:31 +03:00 committed by Lukáš Tinkl
parent e11d522e8e
commit 5ff2976e80
2 changed files with 449 additions and 444 deletions

View File

@ -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

View File

@ -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("&lt;deleted&gt;")
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)
}
}
}
}
]
}
}