2020-11-30 17:03:52 +00:00
|
|
|
import QtQuick 2.13
|
2021-10-26 14:21:08 +00:00
|
|
|
|
2021-10-28 20:23:30 +00:00
|
|
|
import utils 1.0
|
2021-10-27 21:27:49 +00:00
|
|
|
import shared.panels 1.0
|
|
|
|
import shared.status 1.0
|
|
|
|
import shared.controls 1.0
|
2022-07-05 10:12:27 +00:00
|
|
|
import shared.popups 1.0
|
2021-10-28 20:23:30 +00:00
|
|
|
import shared.panels.chat 1.0
|
|
|
|
import shared.views.chat 1.0
|
|
|
|
import shared.controls.chat 1.0
|
2021-09-28 15:04:06 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
import StatusQ.Core 0.1
|
|
|
|
import StatusQ.Core.Theme 0.1
|
|
|
|
import StatusQ.Core.Utils 0.1 as StatusQUtils
|
|
|
|
import StatusQ.Controls 0.1
|
|
|
|
import StatusQ.Components 0.1
|
|
|
|
|
2022-07-04 15:48:04 +00:00
|
|
|
Loader {
|
2021-07-16 15:02:47 +00:00
|
|
|
id: root
|
2022-01-18 21:02:47 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
property var rootStore
|
2021-10-21 22:39:53 +00:00
|
|
|
property var messageStore
|
2022-02-08 12:08:02 +00:00
|
|
|
property var usersStore
|
2022-01-04 12:06:05 +00:00
|
|
|
property var contactsStore
|
2021-12-14 14:19:55 +00:00
|
|
|
property var messageContextMenu
|
2022-03-14 19:32:52 +00:00
|
|
|
property string channelEmoji
|
2022-06-29 16:50:10 +00:00
|
|
|
property bool isActiveChannel: false
|
2021-12-09 12:53:40 +00:00
|
|
|
|
2022-07-25 12:43:05 +00:00
|
|
|
property var chatLogView
|
2022-04-13 09:59:16 +00:00
|
|
|
property var emojiPopup
|
|
|
|
|
2022-02-24 12:15:02 +00:00
|
|
|
// Once we redo qml we will know all section/chat related details in each message form the parent components
|
|
|
|
// without an explicit need to fetch those details via message store/module.
|
|
|
|
property bool isChatBlocked: false
|
|
|
|
|
2022-03-09 10:02:28 +00:00
|
|
|
property int itemIndex: -1
|
2021-12-09 12:53:40 +00:00
|
|
|
property string messageId: ""
|
2022-02-24 15:04:59 +00:00
|
|
|
property string communityId: ""
|
2021-12-09 12:53:40 +00:00
|
|
|
property string responseToMessageWithId: ""
|
|
|
|
property string senderId: ""
|
|
|
|
property string senderDisplayName: ""
|
|
|
|
property string senderLocalName: ""
|
2022-07-05 10:12:27 +00:00
|
|
|
property string senderEnsName: ""
|
2021-12-09 12:53:40 +00:00
|
|
|
property string senderIcon: ""
|
|
|
|
property bool amISender: false
|
2022-03-04 21:33:48 +00:00
|
|
|
property bool senderIsAdded: false
|
2022-06-28 18:11:18 +00:00
|
|
|
property int senderTrustStatus: Constants.trustStatus.unknown
|
2022-03-30 15:30:28 +00:00
|
|
|
readonly property string senderIconToShow: {
|
|
|
|
if ((!senderIsAdded &&
|
2022-07-05 10:12:27 +00:00
|
|
|
Global.privacyModuleInst.profilePicturesVisibility !==
|
|
|
|
Constants.profilePicturesVisibility.everyone)) {
|
2022-03-30 15:30:28 +00:00
|
|
|
return ""
|
2022-03-04 21:33:48 +00:00
|
|
|
}
|
|
|
|
return senderIcon
|
|
|
|
}
|
2022-07-05 10:12:27 +00:00
|
|
|
property string messageText: ""
|
2021-12-09 12:53:40 +00:00
|
|
|
property string messageImage: ""
|
2022-07-05 10:12:27 +00:00
|
|
|
property double messageTimestamp: 0 // We use double, because QML's int is too small
|
2021-12-09 12:53:40 +00:00
|
|
|
property string messageOutgoingStatus: ""
|
|
|
|
property int messageContentType: 1
|
|
|
|
property bool pinnedMessage: false
|
2022-01-05 15:50:03 +00:00
|
|
|
property string messagePinnedBy: ""
|
2021-12-29 10:32:43 +00:00
|
|
|
property var reactionsModel: []
|
2022-01-25 12:56:53 +00:00
|
|
|
property string linkUrls: ""
|
2022-02-09 00:04:49 +00:00
|
|
|
property var transactionParams
|
2021-12-09 12:53:40 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
// External behavior changers
|
|
|
|
property bool isInPinnedPopup: false // The pinned popup limits the number of buttons shown
|
|
|
|
property bool disableHover: false // Used to force the HoverHandler to be active (useful for messages in popups)
|
|
|
|
property bool placeholderMessage: false
|
|
|
|
property bool activityCenterMessage: false
|
|
|
|
property bool activityCenterMessageRead: true
|
|
|
|
|
2022-02-25 10:42:32 +00:00
|
|
|
property int gapFrom: 0
|
|
|
|
property int gapTo: 0
|
|
|
|
|
2021-12-09 12:53:40 +00:00
|
|
|
property int prevMessageIndex: -1
|
|
|
|
property var prevMessageAsJsonObj
|
|
|
|
property int nextMessageIndex: -1
|
|
|
|
property var nextMessageAsJsonObj
|
|
|
|
|
2022-01-17 18:46:46 +00:00
|
|
|
property bool editModeOn: false
|
2022-07-05 10:12:27 +00:00
|
|
|
property bool isEdited: false
|
2022-01-17 18:46:46 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
property string responseTo: responseToMessageWithId
|
2021-12-09 12:53:40 +00:00
|
|
|
|
|
|
|
// Legacy
|
|
|
|
property bool isCurrentUser: amISender
|
|
|
|
property string displayUserName: senderDisplayName
|
|
|
|
property string outgoingStatus: messageOutgoingStatus
|
|
|
|
property string authorCurrentMsg: senderId
|
|
|
|
property string authorPrevMsg: {
|
2022-02-23 20:40:52 +00:00
|
|
|
if(!prevMessageAsJsonObj ||
|
2022-07-05 10:12:27 +00:00
|
|
|
// The system message for private groups appear as created by the group host, but it shouldn't
|
|
|
|
prevMessageAsJsonObj.contentType === Constants.messageContentType.systemMessagePrivateGroupType) {
|
2021-12-09 12:53:40 +00:00
|
|
|
return ""
|
2022-03-09 10:27:32 +00:00
|
|
|
}
|
2021-12-09 12:53:40 +00:00
|
|
|
|
|
|
|
return prevMessageAsJsonObj.senderId
|
|
|
|
}
|
2022-07-05 10:12:27 +00:00
|
|
|
property double prevMsgTimestamp: prevMessageAsJsonObj ? prevMessageAsJsonObj.timestamp : 0
|
|
|
|
property double nextMsgTimestamp: nextMessageAsJsonObj ? nextMessageAsJsonObj.timestamp : 0
|
2021-12-09 12:53:40 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
property bool shouldRepeatHeader: ((messageTimestamp - prevMsgTimestamp) / 60 / 1000) > Constants.repeatHeaderInterval
|
2021-10-21 22:39:53 +00:00
|
|
|
|
2021-02-01 18:40:55 +00:00
|
|
|
property bool hasMention: false
|
2022-01-05 15:50:03 +00:00
|
|
|
property bool stickersLoaded: false
|
2022-07-05 10:12:27 +00:00
|
|
|
|
2021-12-09 12:53:40 +00:00
|
|
|
|
2022-01-13 19:25:38 +00:00
|
|
|
property string sticker: "Qme8vJtyrEHxABcSVGPF95PtozDgUyfr1xGjePmFdZgk9v"
|
|
|
|
property int stickerPack: -1
|
2022-07-05 10:12:27 +00:00
|
|
|
|
|
|
|
property bool isEmoji: messageContentType === Constants.messageContentType.emojiType
|
|
|
|
property bool isImage: messageContentType === Constants.messageContentType.imageType
|
|
|
|
property bool isAudio: messageContentType === Constants.messageContentType.audioType
|
|
|
|
property bool isStatusMessage: messageContentType === Constants.messageContentType.systemMessagePrivateGroupType
|
|
|
|
property bool isSticker: messageContentType === Constants.messageContentType.stickerType
|
|
|
|
property bool isText: messageContentType === Constants.messageContentType.messageType || messageContentType === Constants.messageContentType.editType
|
2021-02-01 18:40:55 +00:00
|
|
|
property bool isMessage: isEmoji || isImage || isSticker || isText || isAudio
|
2022-07-05 10:12:27 +00:00
|
|
|
|| messageContentType === Constants.messageContentType.communityInviteType || messageContentType === Constants.messageContentType.transactionType
|
2020-06-10 15:14:12 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
property bool isExpired: (outgoingStatus === "sending" && (Math.floor(messageTimestamp) + 180000) < Date.now())
|
2021-07-15 14:59:30 +00:00
|
|
|
property int statusAgeEpoch: 0
|
2020-07-16 17:27:09 +00:00
|
|
|
|
2022-01-18 21:02:47 +00:00
|
|
|
signal imageClicked(var image)
|
2020-07-10 15:37:23 +00:00
|
|
|
property var scrollToBottom: function () {}
|
2020-11-30 17:03:52 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
// WARNING: To much arguments here. Create an object argument.
|
|
|
|
property var messageClickHandler: function(sender, point,
|
|
|
|
isProfileClick,
|
|
|
|
isSticker = false,
|
|
|
|
isImage = false,
|
|
|
|
image = null,
|
|
|
|
isEmoji = false,
|
|
|
|
hideEmojiPicker = false,
|
|
|
|
isReply = false,
|
|
|
|
isRightClickOnImage = false,
|
|
|
|
imageSource = "") {
|
2021-12-14 14:19:55 +00:00
|
|
|
|
|
|
|
if (placeholderMessage || activityCenterMessage) {
|
|
|
|
return
|
|
|
|
}
|
2021-12-09 12:53:40 +00:00
|
|
|
|
2021-12-14 14:19:55 +00:00
|
|
|
messageContextMenu.myPublicKey = userProfile.pubKey
|
2022-01-05 15:50:03 +00:00
|
|
|
messageContextMenu.amIChatAdmin = messageStore.amIChatAdmin()
|
2022-05-10 16:13:36 +00:00
|
|
|
messageContextMenu.pinMessageAllowedForMembers = messageStore.pinMessageAllowedForMembers()
|
2021-12-14 14:19:55 +00:00
|
|
|
messageContextMenu.chatType = messageStore.getChatType()
|
|
|
|
|
|
|
|
messageContextMenu.messageId = root.messageId
|
|
|
|
messageContextMenu.messageSenderId = root.senderId
|
|
|
|
messageContextMenu.messageContentType = root.messageContentType
|
|
|
|
messageContextMenu.pinnedMessage = root.pinnedMessage
|
2022-07-05 10:12:27 +00:00
|
|
|
messageContextMenu.canPin = d.canPin
|
2021-12-14 14:19:55 +00:00
|
|
|
|
|
|
|
messageContextMenu.selectedUserPublicKey = root.senderId
|
|
|
|
messageContextMenu.selectedUserDisplayName = root.senderDisplayName
|
2022-03-04 21:33:48 +00:00
|
|
|
messageContextMenu.selectedUserIcon = root.senderIconToShow
|
2021-12-14 14:19:55 +00:00
|
|
|
|
|
|
|
messageContextMenu.imageSource = imageSource
|
|
|
|
|
|
|
|
messageContextMenu.isProfile = !!isProfileClick
|
|
|
|
messageContextMenu.isRightClickOnImage = isRightClickOnImage
|
2022-05-17 10:40:27 +00:00
|
|
|
messageContextMenu.isEmoji = isEmoji
|
2022-05-17 13:09:00 +00:00
|
|
|
messageContextMenu.isSticker = isSticker
|
2021-12-14 14:19:55 +00:00
|
|
|
messageContextMenu.hideEmojiPicker = hideEmojiPicker
|
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
if (isReply){
|
2021-12-14 14:19:55 +00:00
|
|
|
let obj = messageStore.getMessageByIdAsJson(responseTo)
|
|
|
|
if(!obj)
|
|
|
|
return
|
|
|
|
|
2022-03-09 10:27:32 +00:00
|
|
|
messageContextMenu.messageSenderId = obj.senderId
|
|
|
|
messageContextMenu.selectedUserPublicKey = obj.senderId
|
2021-12-14 14:19:55 +00:00
|
|
|
messageContextMenu.selectedUserDisplayName = obj.senderDisplayName
|
2022-07-21 10:56:06 +00:00
|
|
|
messageContextMenu.selectedUserIcon = obj.senderIcon
|
2021-12-14 14:19:55 +00:00
|
|
|
}
|
2021-12-09 12:53:40 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
messageContextMenu.parent = sender;
|
|
|
|
messageContextMenu.popup(point);
|
2020-06-17 12:53:51 +00:00
|
|
|
}
|
|
|
|
|
2022-01-12 12:55:26 +00:00
|
|
|
signal showReplyArea(string messageId, string author)
|
|
|
|
|
2021-10-01 15:58:36 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
// function showReactionAuthors(fromAccounts, emojiId) {
|
|
|
|
// return root.rootStore.showReactionAuthors(fromAccounts, emojiId)
|
|
|
|
// }
|
2021-10-01 15:58:36 +00:00
|
|
|
|
2022-01-27 11:28:27 +00:00
|
|
|
function startMessageFoundAnimation() {
|
2022-07-04 15:48:04 +00:00
|
|
|
root.item.startMessageFoundAnimation();
|
2022-01-27 11:28:27 +00:00
|
|
|
}
|
2021-10-01 15:58:36 +00:00
|
|
|
/////////////////////////////////////////////
|
|
|
|
|
2021-12-08 21:20:43 +00:00
|
|
|
|
|
|
|
signal openStickerPackPopup(string stickerPackId)
|
2021-12-09 12:53:40 +00:00
|
|
|
// Not Refactored Yet
|
2022-07-05 10:12:27 +00:00
|
|
|
// Connections {
|
|
|
|
// enabled: (!placeholderMessage && !!root.rootStore)
|
|
|
|
// target: !!root.rootStore ? root.rootStore.allContacts : null
|
|
|
|
// onContactChanged: {
|
|
|
|
// if (pubkey === fromAuthor) {
|
|
|
|
// const img = appMain.getProfileImage(userPubKey, isCurrentUser, useLargeImage)
|
|
|
|
// if (img) {
|
|
|
|
// profileImageSource = img
|
|
|
|
// }
|
|
|
|
// } else if (replyMessageIndex > -1 && pubkey === repliedMessageAuthorPubkey) {
|
|
|
|
// const imgReply = appMain.getProfileImage(repliedMessageAuthorPubkey, repliedMessageAuthorIsCurrentUser, false)
|
|
|
|
// if (imgReply) {
|
|
|
|
// repliedMessageUserImage = imgReply
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
z: (typeof chatLogView === "undefined") ? 1 : (chatLogView.count - index)
|
|
|
|
|
|
|
|
sourceComponent: {
|
|
|
|
switch(messageContentType) {
|
|
|
|
case Constants.messageContentType.chatIdentifier:
|
|
|
|
return channelIdentifierComponent
|
|
|
|
case Constants.messageContentType.fetchMoreMessagesButton:
|
|
|
|
return fetchMoreMessagesButtonComponent
|
|
|
|
case Constants.messageContentType.systemMessagePrivateGroupType:
|
|
|
|
return privateGroupHeaderComponent
|
|
|
|
case Constants.messageContentType.gapType:
|
|
|
|
return gapComponent
|
|
|
|
default:
|
|
|
|
return messageComponent
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QtObject {
|
|
|
|
id: d
|
|
|
|
|
|
|
|
readonly property bool canPin: !!messageStore &&
|
|
|
|
messageStore.getNumberOfPinnedMessages() < Constants.maxNumberOfPins
|
|
|
|
readonly property int chatButtonSize: 32
|
|
|
|
|
|
|
|
property string activeMessage
|
|
|
|
readonly property bool isMessageActive: typeof activeMessage !== "undefined" && activeMessage === messageId
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Connections {
|
|
|
|
enabled: d.isMessageActive
|
|
|
|
target: root.messageContextMenu
|
|
|
|
onClosed: {
|
|
|
|
d.setMessageActive(root.messageId, false)
|
|
|
|
}
|
|
|
|
}
|
2021-10-01 15:58:36 +00:00
|
|
|
|
2021-05-10 19:12:26 +00:00
|
|
|
Component {
|
|
|
|
id: gapComponent
|
2021-10-21 00:41:54 +00:00
|
|
|
GapComponent {
|
2022-02-25 10:42:32 +00:00
|
|
|
gapFrom: root.gapFrom
|
|
|
|
gapTo: root.gapTo
|
2021-10-21 00:41:54 +00:00
|
|
|
onClicked: {
|
2022-01-28 23:18:30 +00:00
|
|
|
messageStore.fillGaps(messageId)
|
|
|
|
root.visible = false;
|
|
|
|
root.height = 0;
|
2021-05-10 19:12:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-04 11:55:24 +00:00
|
|
|
Component {
|
|
|
|
id: fetchMoreMessagesButtonComponent
|
2021-10-21 00:41:54 +00:00
|
|
|
FetchMoreMessagesButton {
|
2021-12-10 16:11:18 +00:00
|
|
|
nextMessageIndex: root.nextMessageIndex
|
|
|
|
nextMsgTimestamp: root.nextMsgTimestamp
|
2021-10-21 00:41:54 +00:00
|
|
|
onTimerTriggered: {
|
2022-01-28 23:18:30 +00:00
|
|
|
messageStore.requestMoreMessages();
|
2020-09-04 11:55:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-07-15 21:04:14 +00:00
|
|
|
Component {
|
|
|
|
id: channelIdentifierComponent
|
2021-12-10 16:11:18 +00:00
|
|
|
ChannelIdentifierView {
|
|
|
|
chatName: root.senderDisplayName
|
2022-04-01 11:46:32 +00:00
|
|
|
chatId: root.messageStore.getChatId()
|
2022-02-11 16:01:36 +00:00
|
|
|
chatType: root.messageStore.getChatType()
|
|
|
|
chatColor: root.messageStore.getChatColor()
|
2022-03-14 19:32:52 +00:00
|
|
|
chatEmoji: root.channelEmoji
|
2022-02-11 16:01:36 +00:00
|
|
|
amIChatAdmin: root.messageStore.amIChatAdmin()
|
2022-08-11 10:58:09 +00:00
|
|
|
chatIcon: {
|
|
|
|
if ((root.messageStore.getChatType() === Constants.chatType.privateGroupChat) &&
|
|
|
|
root.messageStore.getChatIcon() !== "") {
|
|
|
|
return root.messageStore.getChatIcon()
|
|
|
|
}
|
|
|
|
return root.senderIconToShow
|
|
|
|
}
|
2020-06-10 18:23:18 +00:00
|
|
|
}
|
2020-05-27 22:59:17 +00:00
|
|
|
}
|
|
|
|
|
2020-07-15 21:04:14 +00:00
|
|
|
// Private group Messages
|
|
|
|
Component {
|
|
|
|
id: privateGroupHeaderComponent
|
|
|
|
StyledText {
|
2020-06-25 20:17:42 +00:00
|
|
|
wrapMode: Text.Wrap
|
2020-11-30 17:03:52 +00:00
|
|
|
text: {
|
2020-09-21 15:47:15 +00:00
|
|
|
return `<html>`+
|
2022-07-05 10:12:27 +00:00
|
|
|
`<head>`+
|
|
|
|
`<style type="text/css">`+
|
|
|
|
`a {`+
|
2020-09-21 15:47:15 +00:00
|
|
|
`color: ${Style.current.textColor};`+
|
|
|
|
`text-decoration: none;`+
|
2022-07-05 10:12:27 +00:00
|
|
|
`}`+
|
|
|
|
`</style>`+
|
|
|
|
`</head>`+
|
|
|
|
`<body>`+
|
|
|
|
`${messageText}`+
|
|
|
|
`</body>`+
|
|
|
|
`</html>`;
|
2020-09-21 15:47:15 +00:00
|
|
|
}
|
2020-07-15 21:04:14 +00:00
|
|
|
visible: isStatusMessage
|
2020-09-21 15:47:15 +00:00
|
|
|
font.pixelSize: 14
|
|
|
|
color: Style.current.secondaryText
|
2020-07-15 21:04:14 +00:00
|
|
|
width: parent.width - 120
|
|
|
|
horizontalAlignment: Text.AlignHCenter
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
|
|
textFormat: Text.RichText
|
2020-12-07 23:38:53 +00:00
|
|
|
topPadding: root.prevMessageIndex === 1 ? Style.current.bigPadding : 0
|
2020-07-09 17:47:36 +00:00
|
|
|
}
|
2020-07-09 15:50:38 +00:00
|
|
|
}
|
2020-05-28 19:32:14 +00:00
|
|
|
|
2020-07-15 21:04:14 +00:00
|
|
|
Component {
|
2022-07-05 10:12:27 +00:00
|
|
|
id: messageComponent
|
|
|
|
|
|
|
|
StatusMessage {
|
|
|
|
id: delegate
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2021-12-14 14:19:55 +00:00
|
|
|
}
|
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
readonly property int contentType: convertContentType(root.messageContentType)
|
|
|
|
readonly property bool isReply: root.responseTo !== ""
|
|
|
|
readonly property var replyMessage: root.messageStore && isReply ? root.messageStore.getMessageByIdAsJson(root.responseTo) : null
|
|
|
|
readonly property string replySenderId: replyMessage ? replyMessage.senderId : ""
|
|
|
|
|
|
|
|
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)
|
2021-07-16 15:02:47 +00:00
|
|
|
}
|
2022-01-18 21:02:47 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
audioMessageInfoText: qsTr("Audio Message")
|
|
|
|
cancelButtonText: qsTr("Cancel")
|
|
|
|
saveButtonText: qsTr("Save")
|
|
|
|
loadingImageText: qsTr("Loading image...")
|
|
|
|
errorLoadingImageText: qsTr("Error loading the image")
|
|
|
|
resendText: qsTr("Resend")
|
|
|
|
pinnedMsgInfoText: 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 ? 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
|
|
|
|
|
|
|
|
overrideBackground: root.activityCenterMessage || root.placeholderMessage
|
|
|
|
overrideBackgroundColor: {
|
|
|
|
if (root.activityCenterMessage && root.activityCenterMessageRead)
|
|
|
|
return Utils.setColorAlpha(Style.current.blue, 0.1);
|
|
|
|
return "transparent";
|
2021-12-08 21:20:43 +00:00
|
|
|
}
|
2022-01-12 12:55:26 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
Global.openLink(link)
|
|
|
|
}
|
|
|
|
|
|
|
|
onProfilePictureClicked: {
|
|
|
|
d.setMessageActive(root.messageId, true);
|
|
|
|
root.messageClickHandler(sender, Qt.point(mouse.x, mouse.y), true);
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
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, "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
messageDetails: StatusMessageDetails {
|
|
|
|
contentType: delegate.contentType
|
|
|
|
messageText: root.messageText
|
|
|
|
messageContent: {
|
|
|
|
switch (delegate.contentType)
|
|
|
|
{
|
|
|
|
case StatusMessage.ContentType.Sticker:
|
|
|
|
return root.sticker;
|
|
|
|
case StatusMessage.ContentType.Image:
|
|
|
|
return root.messageImage;
|
|
|
|
}
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
|
|
|
|
amISender: root.amISender
|
|
|
|
sender.id: root.senderId
|
|
|
|
sender.userName: root.senderDisplayName
|
|
|
|
sender.localName: root.senderLocalName
|
|
|
|
sender.ensName: root.senderEnsName
|
|
|
|
sender.isContact: root.senderIsAdded
|
|
|
|
sender.trustIndicator: root.senderTrustStatus
|
|
|
|
sender.profileImage {
|
|
|
|
width: 40
|
|
|
|
height: 40
|
2022-08-11 11:55:08 +00:00
|
|
|
name: root.senderIcon || ""
|
2022-07-05 10:12:27 +00:00
|
|
|
pubkey: root.senderId
|
|
|
|
colorId: Utils.colorIdForPubkey(root.senderId)
|
|
|
|
colorHash: Utils.getColorHashAsJson(root.senderId)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
replyDetails: StatusMessageDetails {
|
|
|
|
messageText: delegate.replyMessage ? delegate.replyMessage.messageText : ""
|
|
|
|
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 "";
|
|
|
|
}
|
|
|
|
|
|
|
|
amISender: delegate.replyMessage && delegate.replyMessage.amISender
|
|
|
|
sender.id: delegate.replyMessage ? delegate.replyMessage.senderId : ""
|
|
|
|
sender.isContact: delegate.replyMessage && delegate.replyMessage.senderIsAdded
|
|
|
|
sender.userName: delegate.replyMessage ? delegate.replyMessage.senderDisplayName: ""
|
|
|
|
sender.ensName: delegate.replyMessage && delegate.replyMessage.ensVerified ? delegate.replyMessage.senderDisplayName : ""
|
|
|
|
sender.localName: delegate.replyMessage ? delegate.replyMessage.senderLocalName : ""
|
|
|
|
sender.profileImage {
|
|
|
|
width: 20
|
|
|
|
height: 20
|
2022-08-11 11:55:08 +00:00
|
|
|
name: delegate.replyMessage ? delegate.replyMessage.senderIcon : ""
|
2022-07-05 10:12:27 +00:00
|
|
|
pubkey: delegate.replySenderId
|
|
|
|
colorId: Utils.colorIdForPubkey(delegate.replySenderId)
|
|
|
|
colorHash: Utils.getColorHashAsJson(delegate.replySenderId)
|
|
|
|
}
|
2022-01-12 12:55:26 +00:00
|
|
|
}
|
2022-01-18 21:02:47 +00:00
|
|
|
|
2022-07-05 10:12:27 +00:00
|
|
|
statusChatInput: StatusChatInput {
|
|
|
|
id: editTextInput
|
2022-08-12 13:10:20 +00:00
|
|
|
objectName: "editMessageInput"
|
2022-07-05 10:12:27 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Component.onCompleted: {
|
|
|
|
parseMessage(root.messageText);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
linksComponent: Component {
|
|
|
|
LinksMessageView {
|
|
|
|
linkUrls: root.linkUrls
|
|
|
|
container: root
|
|
|
|
messageStore: root.messageStore
|
|
|
|
store: root.rootStore
|
|
|
|
isCurrentUser: root.amISender
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2022-08-25 08:27:20 +00:00
|
|
|
objectName: "replyToMessageButton"
|
2022-07-05 10:12:27 +00:00
|
|
|
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 {
|
2022-08-12 13:10:20 +00:00
|
|
|
objectName: "editMessageButton"
|
2022-07-05 10:12:27 +00:00
|
|
|
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 {
|
2022-08-22 21:12:35 +00:00
|
|
|
objectName: "MessageView_toggleMessagePin"
|
2022-07-05 10:12:27 +00:00
|
|
|
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 (d.canPin) {
|
|
|
|
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(pinnedMessagesPopupComponent, {
|
|
|
|
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 {
|
2022-08-25 08:27:20 +00:00
|
|
|
objectName: "chatDeleteMessageButton"
|
2022-07-05 10:12:27 +00:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Component {
|
|
|
|
id: deleteMessageConfirmationDialogComponent
|
|
|
|
|
|
|
|
ConfirmationDialog {
|
2022-08-25 08:27:20 +00:00
|
|
|
confirmButtonObjectName: "chatButtonsPanelConfirmDeleteMessageButton"
|
2022-07-05 10:12:27 +00:00
|
|
|
header.title: qsTrId("Confirm deleting this message")
|
|
|
|
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()
|
|
|
|
messageStore.deleteMessage(root.messageId)
|
|
|
|
}
|
|
|
|
onClosed: {
|
|
|
|
destroy()
|
|
|
|
}
|
2020-07-10 15:37:23 +00:00
|
|
|
}
|
2020-05-27 22:59:17 +00:00
|
|
|
}
|
|
|
|
}
|