2022-10-07 16:50:31 +03:00
import QtQuick 2.14
import QtQuick . Layouts 1.14
2021-10-26 16:21:08 +02:00
2021-10-28 23:23:30 +03:00
import utils 1.0
2021-10-28 00:27:49 +03:00
import shared . panels 1.0
import shared . status 1.0
import shared . controls 1.0
2022-07-05 14:12:27 +04:00
import shared . popups 1.0
2021-10-28 23:23:30 +03:00
import shared . views . chat 1.0
import shared . controls . chat 1.0
2021-09-28 18:04:06 +03:00
2022-07-05 14:12:27 +04: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 11:48:04 -04:00
Loader {
2021-07-16 18:02:47 +03:00
id: root
2022-01-18 22:02:47 +01:00
2022-07-05 14:12:27 +04:00
property var rootStore
2021-10-22 01:39:53 +03:00
property var messageStore
2022-02-08 13:08:02 +01:00
property var usersStore
2022-01-04 13:06:05 +01:00
property var contactsStore
2022-12-13 18:49:31 +03:00
property var messageContextMenu: null
2022-03-14 15:32:52 -04:00
property string channelEmoji
2022-06-29 12:50:10 -04:00
property bool isActiveChannel: false
2021-12-09 13:53:40 +01:00
2022-07-25 14:43:05 +02:00
property var chatLogView
2022-04-13 12:59:16 +03:00
property var emojiPopup
2022-11-14 15:21:00 -05:00
property var stickersPopup
2022-04-13 12:59:16 +03:00
2022-02-24 13:15:02 +01: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
2021-12-09 13:53:40 +01:00
property string messageId: ""
2022-02-24 10:04:59 -05:00
property string communityId: ""
2022-10-06 16:48:04 +03:00
2021-12-09 13:53:40 +01:00
property string senderId: ""
property string senderDisplayName: ""
2022-09-14 09:35:26 +02:00
property string senderOptionalName: ""
property bool senderIsEnsVerified: false
2021-12-09 13:53:40 +01:00
property string senderIcon: ""
2023-01-10 13:29:24 +02:00
//TODO: provide the sender color hash from nim model in case of ContactVerificationRequest, OngoingContactVerificationRequest or PinnedMessagesPopupremove
property var senderColorHash: senderId != "" ? Utils . getColorHashAsJson ( senderId , senderIsEnsVerified ) : ""
2021-12-09 13:53:40 +01:00
property bool amISender: false
2023-01-12 11:28:45 +02:00
property bool amIChatAdmin: messageStore && messageStore . amIChatAdmin
2022-03-04 16:33:48 -05:00
property bool senderIsAdded: false
2022-06-28 14:11:18 -04:00
property int senderTrustStatus: Constants . trustStatus . unknown
2022-07-05 14:12:27 +04:00
property string messageText: ""
2023-01-06 15:19:27 -05:00
property string unparsedText: ""
2021-12-09 13:53:40 +01:00
property string messageImage: ""
2022-07-05 14:12:27 +04:00
property double messageTimestamp: 0 // We use double, because QML's int is too small
2021-12-09 13:53:40 +01:00
property string messageOutgoingStatus: ""
2022-12-08 16:01:08 -05:00
property string resendError: ""
2022-10-26 10:07:46 +02:00
property int messageContentType: Constants . messageContentType . messageType
2023-03-14 01:37:22 +01:00
2021-12-09 13:53:40 +01:00
property bool pinnedMessage: false
2022-01-05 16:50:03 +01:00
property string messagePinnedBy: ""
2021-12-29 11:32:43 +01:00
property var reactionsModel: [ ]
2022-01-25 13:56:53 +01:00
property string linkUrls: ""
2022-09-15 09:31:38 +02:00
property string messageAttachments: ""
2022-02-09 01:04:49 +01:00
property var transactionParams
2021-12-09 13:53:40 +01:00
2023-01-12 14:23:26 -05:00
property string responseToMessageWithId: ""
property string quotedMessageText: ""
property string quotedMessageFrom: ""
property int quotedMessageContentType: Constants . messageContentType . messageType
property int quotedMessageFromIterator: - 1
2023-01-11 16:16:35 -05:00
property bool quotedMessageDeleted: false
2023-01-10 13:29:24 +02:00
property string quotedMessageAuthorDetailsName: ""
property string quotedMessageAuthorDetailsDisplayName: ""
property string quotedMessageAuthorDetailsThumbnailImage: ""
property bool quotedMessageAuthorDetailsEnsVerified: false
property bool quotedMessageAuthorDetailsIsContact: false
property var quotedMessageAuthorDetailsColorHash
2023-01-12 14:23:26 -05:00
2023-03-06 15:47:35 +06:00
property var album: [ ]
2022-07-05 14:12:27 +04: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
2022-02-25 11:42:32 +01:00
property int gapFrom: 0
property int gapTo: 0
2021-12-09 13:53:40 +01:00
property int prevMessageIndex: - 1
2023-01-10 13:29:24 +02:00
property int prevMessageContentType: prevMessageAsJsonObj ? prevMessageAsJsonObj.contentType : Constants . messageContentType . unknownContentType
property double prevMessageTimestamp: prevMessageAsJsonObj ? prevMessageAsJsonObj.timestamp : 0
property string prevMessageSenderId: prevMessageAsJsonObj ? prevMessageAsJsonObj.senderId : ""
2021-12-09 13:53:40 +01:00
property var prevMessageAsJsonObj
property int nextMessageIndex: - 1
2023-01-20 16:36:55 +01:00
property double nextMessageTimestamp: nextMessageAsJsonObj ? nextMessageAsJsonObj.timestamp : 0
2021-12-09 13:53:40 +01:00
property var nextMessageAsJsonObj
2022-01-17 19:46:46 +01:00
property bool editModeOn: false
2022-07-05 14:12:27 +04:00
property bool isEdited: false
2022-01-17 19:46:46 +01:00
2023-01-10 13:29:24 +02:00
property bool shouldRepeatHeader: d . getShouldRepeatHeader ( messageTimestamp , prevMessageTimestamp , messageOutgoingStatus )
2021-10-22 01:39:53 +03:00
2021-02-01 13:40:55 -05:00
property bool hasMention: false
2021-12-09 13:53:40 +01:00
2022-10-26 10:07:46 +02:00
property bool stickersLoaded: false
2023-02-15 12:12:12 +01:00
property string sticker
2022-01-13 14:25:38 -05:00
property int stickerPack: - 1
2022-07-05 14:12:27 +04:00
property bool isEmoji: messageContentType === Constants . messageContentType . emojiType
2022-09-15 09:31:38 +02:00
property bool isImage: messageContentType === Constants . messageContentType . imageType || ( isDiscordMessage && messageImage != "" )
2022-07-05 14:12:27 +04:00
property bool isAudio: messageContentType === Constants . messageContentType . audioType
property bool isStatusMessage: messageContentType === Constants . messageContentType . systemMessagePrivateGroupType
property bool isSticker: messageContentType === Constants . messageContentType . stickerType
2022-09-15 09:31:38 +02:00
property bool isDiscordMessage: messageContentType === Constants . messageContentType . discordMessageType
2023-03-07 18:02:27 +06:00
property bool isText: messageContentType === Constants . messageContentType . messageType || messageContentType === Constants . messageContentType . contactRequestType || isDiscordMessage
2021-02-01 13:40:55 -05:00
property bool isMessage: isEmoji || isImage || isSticker || isText || isAudio
2022-07-05 14:12:27 +04:00
|| messageContentType === Constants . messageContentType . communityInviteType || messageContentType === Constants . messageContentType . transactionType
2020-06-10 11:14:12 -04:00
2023-01-11 18:12:07 +03:00
readonly property bool isExpired: d . getIsExpired ( messageTimestamp , messageOutgoingStatus )
2022-12-08 16:01:08 -05:00
readonly property bool isSending: messageOutgoingStatus === Constants . sending && ! isExpired
2020-07-16 13:27:09 -04:00
2022-01-18 22:02:47 +01:00
signal imageClicked ( var image )
2020-11-30 12:03:52 -05:00
2022-07-05 14:12:27 +04: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 15:19:55 +01:00
2023-01-06 16:43:54 +01:00
if ( placeholderMessage || ! ( root . rootStore . mainModuleInst . activeSection . joined || isProfileClick ) ) {
2023-03-20 16:59:53 +07:00
return false
2021-12-14 15:19:55 +01:00
}
2021-12-09 13:53:40 +01:00
2021-12-14 15:19:55 +01:00
messageContextMenu . myPublicKey = userProfile . pubKey
2022-12-26 11:25:16 -05:00
messageContextMenu . amIChatAdmin = root . amIChatAdmin
2023-01-12 11:28:45 +02:00
messageContextMenu . pinMessageAllowedForMembers = messageStore . isPinMessageAllowedForMembers
messageContextMenu . chatType = messageStore . chatType
2021-12-14 15:19:55 +01:00
messageContextMenu . messageId = root . messageId
2023-01-06 15:19:27 -05:00
messageContextMenu . unparsedText = root . unparsedText
2021-12-14 15:19:55 +01:00
messageContextMenu . messageSenderId = root . senderId
messageContextMenu . messageContentType = root . messageContentType
messageContextMenu . pinnedMessage = root . pinnedMessage
2022-09-12 18:42:58 +02:00
messageContextMenu . canPin = ! ! root . messageStore && root . messageStore . getNumberOfPinnedMessages ( ) < Constants . maxNumberOfPins
2021-12-14 15:19:55 +01:00
messageContextMenu . selectedUserPublicKey = root . senderId
messageContextMenu . selectedUserDisplayName = root . senderDisplayName
2022-10-06 22:15:15 +02:00
messageContextMenu . selectedUserIcon = root . senderIcon
2021-12-14 15:19:55 +01:00
messageContextMenu . imageSource = imageSource
messageContextMenu . isProfile = ! ! isProfileClick
messageContextMenu . isRightClickOnImage = isRightClickOnImage
2022-05-17 12:40:27 +02:00
messageContextMenu . isEmoji = isEmoji
2022-05-17 16:09:00 +03:00
messageContextMenu . isSticker = isSticker
2021-12-14 15:19:55 +01:00
messageContextMenu . hideEmojiPicker = hideEmojiPicker
2023-01-12 14:23:26 -05:00
if ( isReply ) {
if ( ! quotedMessageFrom ) {
// The responseTo message was deleted so we don't eneble to right click the unaviable profile
2023-03-20 16:59:53 +07:00
return false
2023-01-12 14:23:26 -05:00
}
messageContextMenu . messageSenderId = quotedMessageFrom
messageContextMenu . selectedUserPublicKey = quotedMessageFrom
2023-01-10 13:29:24 +02:00
messageContextMenu . selectedUserDisplayName = quotedMessageAuthorDetailsDisplayName
messageContextMenu . selectedUserIcon = quotedMessageAuthorDetailsThumbnailImage
2021-12-14 15:19:55 +01:00
}
2021-12-09 13:53:40 +01:00
2023-03-20 16:59:53 +07:00
// Emoji container is not a menu item of messageContextMenu so checking it separatly
if ( messageContextMenu . checkIfEmpty ( ) && ! isEmoji ) {
return false
}
messageContextMenu . parent = sender
messageContextMenu . popup ( point )
return true
2020-06-17 08:53:51 -04:00
}
2022-01-12 15:55:26 +03:00
signal showReplyArea ( string messageId , string author )
2021-10-01 18:58:36 +03:00
2022-01-27 12:28:27 +01:00
function startMessageFoundAnimation ( ) {
2022-07-04 11:48:04 -04:00
root . item . startMessageFoundAnimation ( ) ;
2022-01-27 12:28:27 +01:00
}
2021-10-01 18:58:36 +03:00
2021-12-08 23:20:43 +02:00
signal openStickerPackPopup ( string stickerPackId )
2022-07-05 14:12:27 +04:00
z: ( typeof chatLogView === "undefined" ) ? 1 : ( chatLogView . count - index )
2022-12-05 08:52:41 +01:00
asynchronous: true
2022-07-05 14:12:27 +04:00
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
2022-12-05 08:52:41 +01:00
case Constants.messageContentType.newMessagesMarker:
return newMessagesMarkerComponent
2022-07-05 14:12:27 +04:00
default:
return messageComponent
}
}
QtObject {
id: d
readonly property int chatButtonSize: 32
2023-02-03 15:05:32 +02:00
property bool hideMessage: false
2022-09-20 16:14:58 +02:00
2022-07-05 14:12:27 +04:00
property string activeMessage
2023-01-06 16:43:54 +01:00
readonly property bool isMessageActive: d . activeMessage === root . messageId
2022-07-05 14:12:27 +04:00
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 ;
}
}
2022-12-20 15:21:33 +02:00
2023-02-15 12:12:12 +01:00
function nextMessageHasHeader ( ) {
if ( ! root . nextMessageAsJsonObj ) {
return false
}
return root . senderId !== root . nextMessageAsJsonObj . senderId ||
d . getShouldRepeatHeader ( root . nextMessageAsJsonObj . timeStamp , root . messageTimestamp , root . nextMessageAsJsonObj . outgoingStatus ) ||
root . nextMessageAsJsonObj . responseToMessageWithId !== ""
}
2022-12-20 15:21:33 +02:00
function getShouldRepeatHeader ( messageTimeStamp , prevMessageTimeStamp , messageOutgoingStatus ) {
return ( ( messageTimeStamp - prevMessageTimeStamp ) / 60 / 1000 ) > Constants . repeatHeaderInterval
|| d . getIsExpired ( messageTimeStamp , messageOutgoingStatus )
}
function getIsExpired ( messageTimeStamp , messageOutgoingStatus ) {
return ( messageOutgoingStatus === Constants . sending && ( Math . floor ( messageTimeStamp ) + 180000 ) < Date . now ( ) ) || messageOutgoingStatus === Constants . expired
}
2023-02-15 12:12:12 +01:00
function convertContentType ( value ) {
switch ( value ) {
2023-03-07 18:02:27 +06:00
case Constants.messageContentType.contactRequestType:
2023-02-15 12:12:12 +01:00
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.discordMessageType:
return StatusMessage . ContentType . DiscordMessage ;
case Constants.messageContentType.fetchMoreMessagesButton:
case Constants.messageContentType.chatIdentifier:
case Constants.messageContentType.unknownContentType:
case Constants.messageContentType.statusType:
case Constants.messageContentType.systemMessagePrivateGroupType:
case Constants.messageContentType.gapType:
default:
return StatusMessage . ContentType . Unknown ;
}
}
2022-07-05 14:12:27 +04:00
}
2022-09-20 16:14:58 +02:00
2022-07-05 14:12:27 +04:00
Connections {
enabled: d . isMessageActive
target: root . messageContextMenu
2022-09-23 00:01:29 +02:00
function onClosed ( ) {
2022-07-05 14:12:27 +04:00
d . setMessageActive ( root . messageId , false )
}
}
2021-10-01 18:58:36 +03:00
2021-05-10 15:12:26 -04:00
Component {
id: gapComponent
2021-10-21 03:41:54 +03:00
GapComponent {
2022-02-25 11:42:32 +01:00
gapFrom: root . gapFrom
gapTo: root . gapTo
2021-10-21 03:41:54 +03:00
onClicked: {
2022-01-28 19:18:30 -04:00
messageStore . fillGaps ( messageId )
root . visible = false ;
root . height = 0 ;
2021-05-10 15:12:26 -04:00
}
}
}
2020-09-04 13:55:24 +02:00
Component {
id: fetchMoreMessagesButtonComponent
2021-10-21 03:41:54 +03:00
FetchMoreMessagesButton {
2021-12-10 17:11:18 +01:00
nextMessageIndex: root . nextMessageIndex
2023-01-10 13:29:24 +02:00
nextMsgTimestamp: root . nextMessageTimestamp
2021-10-21 03:41:54 +03:00
onTimerTriggered: {
2022-01-28 19:18:30 -04:00
messageStore . requestMoreMessages ( ) ;
2020-09-04 13:55:24 +02:00
}
}
}
2020-07-15 17:04:14 -04:00
Component {
id: channelIdentifierComponent
2021-12-10 17:11:18 +01:00
ChannelIdentifierView {
chatName: root . senderDisplayName
2022-04-01 13:46:32 +02:00
chatId: root . messageStore . getChatId ( )
2023-01-12 11:28:45 +02:00
chatType: root . messageStore . chatType
chatColor: root . messageStore . chatColor
2022-03-14 15:32:52 -04:00
chatEmoji: root . channelEmoji
2022-12-26 11:25:16 -05:00
amIChatAdmin: root . amIChatAdmin
2022-08-11 12:58:09 +02:00
chatIcon: {
2023-01-12 11:28:45 +02:00
if ( root . messageStore . chatType === Constants . chatType . privateGroupChat &&
root . messageStore . chatIcon !== "" ) {
return root . messageStore . chatIcon
2022-08-11 12:58:09 +02:00
}
2022-10-06 22:15:15 +02:00
return root . senderIcon
2022-08-11 12:58:09 +02:00
}
2020-06-10 14:23:18 -04:00
}
2020-05-27 18:59:17 -04:00
}
2020-07-15 17:04:14 -04:00
// Private group Messages
Component {
id: privateGroupHeaderComponent
StyledText {
2020-06-25 16:17:42 -04:00
wrapMode: Text . Wrap
2020-11-30 12:03:52 -05:00
text: {
2020-09-21 11:47:15 -04:00
return ` < html > ` +
2022-07-05 14:12:27 +04:00
` < head > ` +
` < style type = "text/css" > ` +
` a { ` +
2020-09-21 11:47:15 -04:00
` color: $ { Style . current . textColor } ; ` +
` text - decoration: none ; ` +
2022-07-05 14:12:27 +04:00
` } ` +
` < / s t y l e > ` +
` < / h e a d > ` +
` < body > ` +
` $ { messageText } ` +
` < / b o d y > ` +
` < / h t m l > ` ;
2020-09-21 11:47:15 -04:00
}
2020-07-15 17:04:14 -04:00
visible: isStatusMessage
2020-09-21 11:47:15 -04:00
font.pixelSize: 14
color: Style . current . secondaryText
2020-07-15 17:04:14 -04:00
width: parent . width - 120
horizontalAlignment: Text . AlignHCenter
anchors.horizontalCenter: parent . horizontalCenter
textFormat: Text . RichText
2020-12-08 10:38:53 +11:00
topPadding: root . prevMessageIndex === 1 ? Style.current.bigPadding : 0
2020-07-09 13:47:36 -04:00
}
2020-07-09 11:50:38 -04:00
}
2020-05-28 15:32:14 -04:00
2022-10-07 16:50:31 +03:00
2020-07-15 17:04:14 -04:00
Component {
2022-07-05 14:12:27 +04:00
id: messageComponent
2022-10-07 16:50:31 +03:00
ColumnLayout {
spacing: 0
2022-10-26 10:07:46 +02:00
function startMessageFoundAnimation ( ) {
delegate . startMessageFoundAnimation ( ) ;
}
2022-10-07 16:50:31 +03:00
StatusDateGroupLabel {
id: dateGroupLabel
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
messageTimestamp: root . messageTimestamp
2023-01-10 13:29:24 +02:00
previousMessageTimestamp: root . prevMessageIndex === - 1 ? 0 : root . prevMessageTimestamp
2023-02-15 12:12:12 +01:00
visible: text !== "" && ! root . isInPinnedPopup
2021-12-14 15:19:55 +01:00
}
2022-10-07 16:50:31 +03:00
StatusMessage {
id: delegate
Layout.fillWidth: true
2023-02-15 12:12:12 +01:00
Layout.topMargin: showHeader && ! root . isInPinnedPopup ? 2 : 0
Layout.bottomMargin: ! root . isInPinnedPopup ? 2 : 0
2022-10-07 17:17:24 +03:00
2023-02-15 12:12:12 +01:00
readonly property int contentType: d . convertContentType ( root . messageContentType )
2022-12-21 18:04:25 +03:00
property string originalMessageText: ""
function editCancelledHandler ( ) {
root . messageStore . setEditModeOff ( root . messageId )
}
2022-10-07 16:50:31 +03:00
function editCompletedHandler ( newMessageText ) {
2022-12-21 18:04:25 +03:00
if ( delegate . originalMessageText === newMessageText ) {
delegate . editCancelledHandler ( )
return
}
2022-10-07 16:50:31 +03:00
const message = root . rootStore . plainText ( StatusQUtils . Emoji . deparse ( newMessageText ) )
2022-12-21 18:04:25 +03:00
2022-10-07 16:50:31 +03:00
if ( message . length <= 0 )
return ;
2022-01-18 22:02:47 +01:00
2022-10-07 16:50:31 +03:00
const interpretedMessage = root . messageStore . interpretMessage ( message )
root . messageStore . setEditModeOff ( root . messageId )
2022-11-08 18:44:05 +03:00
root . messageStore . editMessage ( root . messageId , root . messageContentType , interpretedMessage )
2022-10-07 16:50:31 +03:00
}
2022-07-05 14:12:27 +04:00
2022-10-07 16:50:31 +03:00
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
2023-02-15 12:12:12 +01:00
isAReply: root . responseToMessageWithId !== ""
2022-10-07 16:50:31 +03:00
isEdited: root . isEdited
hasMention: root . hasMention
isPinned: root . pinnedMessage
2022-12-13 12:37:27 +03:00
pinnedBy: {
if ( ! root . pinnedMessage || root . isDiscordMessage )
return ""
const contact = Utils . getContactDetailsAsJson ( root . messagePinnedBy , false )
const ensName = contact . ensVerified ? contact.name : ""
return ProfileUtils . displayName ( contact . localNickname , ensName , contact . displayName , contact . alias )
}
2023-02-15 12:12:12 +01:00
isInPinnedPopup: root . isInPinnedPopup
2022-10-07 16:50:31 +03:00
hasExpired: root . isExpired
2022-12-08 16:01:08 -05:00
isSending: root . isSending
resendError: root . resendError
2022-10-07 16:50:31 +03:00
reactionsModel: root . reactionsModel
2023-01-10 13:29:24 +02:00
showHeader: root . shouldRepeatHeader || dateGroupLabel . visible || isAReply ||
2023-03-14 01:37:22 +01:00
root . prevMessageContentType === Constants . messageContentType . systemMessagePrivateGroupType ||
root . senderId !== root . prevMessageSenderId
2022-10-07 16:50:31 +03:00
isActiveMessage: d . isMessageActive
2023-02-15 12:12:12 +01:00
topPadding: showHeader ? Style.current.halfPadding : 0
bottomPadding: showHeader && d . nextMessageHasHeader ( ) ? Style.current.halfPadding : 2
2022-10-07 16:50:31 +03:00
disableHover: root . disableHover ||
2023-01-10 14:07:24 +02:00
( root . chatLogView && root . chatLogView . moving ) ||
2022-12-13 18:49:31 +03:00
( root . messageContextMenu && root . messageContextMenu . opened ) ||
2023-01-06 16:43:54 +01:00
Global . popupOpened
2022-10-07 16:50:31 +03:00
hideQuickActions: root . isChatBlocked ||
root . placeholderMessage ||
2022-11-21 17:07:18 +01:00
root . isInPinnedPopup ||
2022-11-29 11:04:47 +02:00
root . editModeOn ||
! root . rootStore . mainModuleInst . activeSection . joined
2023-03-20 16:59:53 +07:00
disableEmojis: root . isChatBlocked
2023-02-03 15:05:32 +02:00
hideMessage: d . hideMessage
2022-10-07 16:50:31 +03:00
2023-01-06 16:43:54 +01:00
overrideBackground: root . placeholderMessage
2022-10-07 16:50:31 +03:00
profileClickable: ! root . isDiscordMessage
messageAttachments: root . messageAttachments
2022-07-05 14:12:27 +04:00
2022-10-07 16:50:31 +03:00
onEditCancelled: {
2022-12-21 18:04:25 +03:00
delegate . editCancelledHandler ( )
2022-10-07 16:50:31 +03:00
}
2022-07-05 14:12:27 +04:00
2023-03-29 14:50:00 -04:00
onEditCompleted: delegate . editCompletedHandler ( newMsgText )
2022-07-05 14:12:27 +04:00
2022-10-07 16:50:31 +03:00
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 ;
}
2022-07-05 14:12:27 +04:00
}
2022-10-07 16:50:31 +03:00
onLinkActivated: {
if ( link . startsWith ( '//' ) ) {
const pubkey = link . replace ( "//" , "" ) ;
Global . openProfilePopup ( pubkey )
2022-10-20 11:05:10 +02:00
return
} else if ( link . startsWith ( '#' ) ) {
2022-10-07 16:50:31 +03:00
rootStore . chatCommunitySectionModule . switchToChannel ( link . replace ( "#" , "" ) )
2022-10-20 11:05:10 +02:00
return
2022-11-04 15:05:32 +03:00
} else if ( Utils . isStatusDeepLink ( link ) ) {
2022-10-20 11:05:10 +02:00
rootStore . activateStatusDeepLink ( link )
return
2022-10-07 16:50:31 +03:00
}
2022-07-05 14:12:27 +04:00
2022-10-07 16:50:31 +03:00
Global . openLink ( link )
}
2022-07-05 14:12:27 +04:00
2022-10-07 16:50:31 +03:00
onProfilePictureClicked: {
2023-03-20 16:59:53 +07:00
if ( root . messageClickHandler ( sender , Qt . point ( mouse . x , mouse . y ) , true ) )
d . setMessageActive ( root . messageId , true )
2022-10-07 16:50:31 +03:00
}
2022-07-05 14:12:27 +04:00
2022-10-07 16:50:31 +03:00
onReplyProfileClicked: {
2023-03-20 16:59:53 +07:00
if ( root . messageClickHandler ( sender , Qt . point ( mouse . x , mouse . y ) , true , false , false , null , false , false , true ) )
d . setMessageActive ( root . messageId , true )
2022-10-07 16:50:31 +03:00
}
2022-07-05 14:12:27 +04:00
2022-11-08 20:34:41 +03:00
onReplyMessageClicked: {
root . messageStore . messageModule . jumpToMessage ( root . responseToMessageWithId )
}
2022-10-07 16:50:31 +03:00
onSenderNameClicked: {
2023-03-20 16:59:53 +07:00
if ( root . messageClickHandler ( sender , Qt . point ( mouse . x , mouse . y ) , true ) )
d . setMessageActive ( root . messageId , true )
2022-07-05 14:12:27 +04:00
}
2022-10-07 16:50:31 +03:00
onToggleReactionClicked: {
if ( root . isChatBlocked )
return
2022-07-05 14:12:27 +04:00
2022-10-07 16:50:31 +03:00
if ( ! root . messageStore ) {
console . error ( "Reaction can not be toggled, message store is not valid" )
return
}
2022-07-05 14:12:27 +04:00
2022-10-07 16:50:31 +03:00
root . messageStore . toggleReaction ( root . messageId , emojiId )
}
2022-07-05 14:12:27 +04:00
2022-10-07 16:50:31 +03:00
onAddReactionClicked: {
if ( root . isChatBlocked )
2023-03-20 16:59:53 +07:00
return
2022-07-05 14:12:27 +04:00
2023-03-20 16:59:53 +07:00
if ( root . messageClickHandler ( sender , Qt . point ( mouse . x , mouse . y ) , false , false , false , null , true , false ) )
d . setMessageActive ( root . messageId , true )
2022-07-05 14:12:27 +04:00
}
2022-10-07 16:50:31 +03:00
onStickerClicked: {
root . openStickerPackPopup ( root . stickerPack ) ;
}
2022-12-08 16:01:08 -05:00
onResendClicked: {
root . messageStore . resendMessage ( root . messageId )
}
2022-10-07 16:50:31 +03:00
mouseArea {
2023-01-06 16:43:54 +01:00
acceptedButtons: Qt . RightButton
2022-10-07 16:50:31 +03:00
enabled: ! root . isChatBlocked &&
! root . placeholderMessage &&
delegate . contentType !== StatusMessage . ContentType . Image
onClicked: {
2023-03-20 16:59:53 +07:00
if ( root . messageClickHandler ( this , Qt . point ( mouse . x , mouse . y ) ,
false , false , false , null , root . isEmoji , false , false , false , "" ) )
d . setMessageActive ( root . messageId , true )
2022-09-15 09:31:38 +02:00
}
2022-07-05 14:12:27 +04:00
}
2022-10-07 16:50:31 +03:00
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:
2023-03-06 15:47:35 +06:00
2022-10-07 16:50:31 +03:00
return root . messageImage ;
}
if ( root . isDiscordMessage && root . messageImage != "" ) {
2023-02-15 12:12:12 +01:00
return root . messageImage
2022-10-07 16:50:31 +03:00
}
return "" ;
}
2023-03-06 15:47:35 +06:00
album: root . album
2022-10-07 16:50:31 +03:00
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 )
2023-01-10 13:29:24 +02:00
colorHash: root . senderColorHash
2022-12-01 11:24:25 +01:00
showRing: ! root . isDiscordMessage && ! root . senderIsEnsVerified
2022-10-07 16:50:31 +03:00
}
2022-07-05 14:12:27 +04:00
}
2022-10-07 16:50:31 +03:00
replyDetails: StatusMessageDetails {
2023-01-11 16:16:35 -05:00
messageText: {
if ( root . quotedMessageDeleted ) {
return qsTr ( "Message deleted" )
}
if ( ! root . quotedMessageText ) {
return qsTr ( "Unknown message. Try fetching more messages" )
}
return root . quotedMessageText
}
2023-02-15 12:12:12 +01:00
contentType: d . convertContentType ( root . quotedMessageContentType )
2022-10-07 16:50:31 +03:00
messageContent: {
2023-01-12 14:23:26 -05:00
if ( contentType !== StatusMessage . ContentType . Sticker && contentType !== StatusMessage . ContentType . Image ) {
return ""
}
let message = root . messageStore . getMessageByIdAsJson ( responseToMessageWithId )
2022-10-07 16:50:31 +03:00
switch ( contentType ) {
case StatusMessage.ContentType.Sticker:
2023-01-12 14:23:26 -05:00
return message . sticker ;
2022-10-07 16:50:31 +03:00
case StatusMessage.ContentType.Image:
2023-01-12 14:23:26 -05:00
return message . messageImage ;
2022-10-07 16:50:31 +03:00
}
2022-07-05 14:12:27 +04:00
return "" ;
}
2023-01-12 14:23:26 -05:00
amISender: root . quotedMessageFrom === userProfile . pubKey
sender.id: root . quotedMessageFrom
2023-01-10 13:29:24 +02:00
sender.isContact: quotedMessageAuthorDetailsIsContact
sender.displayName: quotedMessageAuthorDetailsDisplayName
sender.isEnsVerified: quotedMessageAuthorDetailsEnsVerified
2023-01-20 16:33:21 +01:00
sender.secondaryName: quotedMessageAuthorDetailsName
2022-10-07 16:50:31 +03:00
sender . profileImage {
width: 20
height: 20
2023-01-10 13:29:24 +02:00
name: quotedMessageAuthorDetailsThumbnailImage
assetSettings.isImage: quotedMessageAuthorDetailsThumbnailImage
2023-01-12 14:23:26 -05:00
showRing: ( root . quotedMessageContentType !== Constants . messageContentType . discordMessageType ) && ! sender . isEnsVerified
pubkey: sender . id
colorId: Utils . colorIdForPubkey ( sender . id )
2023-01-10 13:29:24 +02:00
colorHash: quotedMessageAuthorDetailsColorHash
2022-10-07 16:50:31 +03:00
}
2022-07-05 14:12:27 +04:00
}
2022-01-18 22:02:47 +01:00
2023-01-06 16:43:54 +01:00
statusChatInput: Component {
StatusChatInput {
id: editTextInput
objectName: "editMessageInput"
2022-07-05 14:12:27 +04:00
2023-01-06 16:43:54 +01:00
readonly property string messageText: editTextInput . textInput . text
2022-07-05 14:12:27 +04:00
2023-01-06 16:43:54 +01:00
// TODO: Move this property and Escape handler to StatusChatInput
property bool suggestionsOpened: false
2022-07-05 14:12:27 +04:00
2023-01-06 16:43:54 +01:00
width: parent . width
2022-07-05 14:12:27 +04:00
2023-01-06 16:43:54 +01:00
Keys.onEscapePressed: {
if ( ! suggestionsOpened ) {
delegate . editCancelled ( )
}
suggestionsOpened = false
2022-10-07 16:50:31 +03:00
}
2022-07-05 14:12:27 +04:00
2023-01-06 16:43:54 +01:00
store: root . rootStore
usersStore: root . usersStore
emojiPopup: root . emojiPopup
stickersPopup: root . stickersPopup
messageContextMenu: root . messageContextMenu
2022-07-05 14:12:27 +04:00
2023-01-12 11:28:45 +02:00
chatType: root . messageStore . chatType
2023-01-06 16:43:54 +01:00
isEdit: true
2022-10-07 16:50:31 +03:00
2023-03-29 14:50:00 -04:00
onSendMessage: delegate . editCompletedHandler ( editTextInput . getTextWithPublicKeys ( ) )
2022-07-05 14:12:27 +04:00
2023-01-06 16:43:54 +01:00
Component.onCompleted: {
parseMessage ( root . messageText ) ;
delegate . originalMessageText = editTextInput . textInput . text
}
2022-07-05 14:12:27 +04:00
}
}
2023-02-03 15:05:32 +02:00
hasLinks: ! ! root . linkUrls
2022-10-07 16:50:31 +03:00
linksComponent: Component {
LinksMessageView {
2023-02-03 15:05:32 +02:00
id: linksMessageView
links: root . linkUrls
2022-10-07 16:50:31 +03:00
messageStore: root . messageStore
store: root . rootStore
isCurrentUser: root . amISender
onImageClicked: {
root . imageClicked ( image ) ;
}
2023-02-03 15:05:32 +02:00
onLinksLoaded: {
// If there is only one image and no links, hide the message
// Handled in linksLoaded signal to evaulate it only once
2023-02-15 15:48:43 +02:00
d . hideMessage = linksMessageView . unfurledImagesCount === 1 && linksMessageView . unfurledLinksCount === 0
&& ` < p > $ { root . linkUrls } < / p > ` = = = r o o t . m e s s a g e T e x t
2023-02-03 15:05:32 +02:00
}
2022-08-31 09:32:08 +01:00
}
2022-07-05 14:12:27 +04:00
}
2022-10-07 16:50:31 +03:00
transcationComponent: Component {
TransactionBubbleView {
transactionParams: root . transactionParams
store: root . rootStore
contactsStore: root . contactsStore
}
2022-07-05 14:12:27 +04:00
}
2022-10-07 16:50:31 +03:00
invitationComponent: Component {
InvitationBubbleView {
store: root . rootStore
communityId: root . communityId
}
2022-07-05 14:12:27 +04:00
}
2022-10-07 16:50:31 +03:00
quickActions: [
Loader {
2023-01-12 16:32:51 +02:00
active: ! root . isInPinnedPopup && delegate . hovered
2023-01-12 11:28:45 +02:00
visible: active
2022-10-07 16:50:31 +03:00
sourceComponent: StatusFlatRoundButton {
width: d . chatButtonSize
height: d . chatButtonSize
icon.name: "reaction-b"
type: StatusFlatRoundButton . Type . Tertiary
tooltip.text: qsTr ( "Add reaction" )
onClicked: {
2023-03-20 16:59:53 +07:00
if ( root . messageClickHandler ( delegate , mapToItem ( delegate , mouse . x , mouse . y ) , false , false , false , null , true , false ) )
d . setMessageActive ( root . messageId , true )
2022-07-05 14:12:27 +04:00
}
}
2022-10-07 16:50:31 +03:00
} ,
Loader {
2023-01-12 16:32:51 +02:00
active: ! root . isInPinnedPopup && delegate . hovered
2023-01-12 11:28:45 +02:00
visible: active
2022-10-07 16:50:31 +03:00
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 ( )
}
}
2022-07-05 14:12:27 +04:00
}
2022-10-07 16:50:31 +03:00
} ,
Loader {
2023-01-12 16:32:51 +02:00
active: ! root . isInPinnedPopup && root . isText && ! root . editModeOn && root . amISender && delegate . hovered
2022-10-07 16:50:31 +03:00
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 )
2022-07-05 14:12:27 +04:00
}
2022-10-07 16:50:31 +03:00
}
} ,
Loader {
active: {
2023-01-12 16:32:51 +02:00
if ( ! delegate . hovered )
return false ;
2022-10-07 16:50:31 +03:00
if ( ! root . messageStore )
return false
2022-07-05 14:12:27 +04:00
2023-01-12 11:28:45 +02:00
const chatType = root . messageStore . chatType ;
const pinMessageAllowedForMembers = root . messageStore . isPinMessageAllowedForMembers
2022-07-05 14:12:27 +04:00
2022-10-07 16:50:31 +03:00
return chatType === Constants . chatType . oneToOne ||
2022-12-26 11:25:16 -05:00
chatType === Constants . chatType . privateGroupChat && root . amIChatAdmin ||
chatType === Constants . chatType . communityChat && ( root . amIChatAdmin || pinMessageAllowedForMembers ) ;
2022-07-05 14:12:27 +04:00
}
2023-01-12 11:28:45 +02:00
visible: active
2022-10-07 16:50:31 +03:00
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 ;
}
2023-03-27 20:55:26 +04:00
const chatId = root . messageStore . chatType === Constants . chatType . oneToOne ? chatContentModule . getMyChatId ( ) : ""
Global . openPinnedMessagesPopupRequested ( root . rootStore , messageStore , chatContentModule . pinnedMessagesModel , root . messageId , chatId )
2022-07-05 14:12:27 +04:00
}
2022-10-07 16:50:31 +03:00
}
} ,
Loader {
active: {
2023-01-12 16:32:51 +02:00
if ( ! delegate . hovered )
return false ;
2022-10-07 16:50:31 +03:00
if ( root . isInPinnedPopup )
return false ;
if ( ! root . messageStore )
return false ;
2022-12-26 11:25:16 -05:00
return ( root . amISender || root . amIChatAdmin ) &&
2022-10-07 16:50:31 +03:00
( messageContentType === Constants . messageContentType . messageType ||
messageContentType === Constants . messageContentType . stickerType ||
messageContentType === Constants . messageContentType . emojiType ||
messageContentType === Constants . messageContentType . imageType ||
messageContentType === Constants . messageContentType . audioType ) ;
}
2023-01-12 11:28:45 +02:00
visible: active
2022-10-07 16:50:31 +03:00
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 )
}
2022-07-05 14:12:27 +04:00
}
}
}
2022-10-07 16:50:31 +03:00
]
}
2022-07-05 14:12:27 +04:00
}
}
Component {
id: deleteMessageConfirmationDialogComponent
ConfirmationDialog {
2022-08-25 11:27:20 +03:00
confirmButtonObjectName: "chatButtonsPanelConfirmDeleteMessageButton"
2022-10-18 23:59:46 +02:00
header.title: qsTr ( "Confirm deleting this message" )
confirmationText: qsTr ( "Are you sure you want to delete this message? Be aware that other clients are not guaranteed to delete the message as well." )
2022-07-05 14:12:27 +04:00
height: 260
checkbox.visible: true
executeConfirm: function ( ) {
if ( checkbox . checked ) {
localAccountSensitiveSettings . showDeleteMessageWarning = false
}
close ( )
messageStore . deleteMessage ( root . messageId )
}
onClosed: {
destroy ( )
}
2020-07-10 11:37:23 -04:00
}
2020-05-27 18:59:17 -04:00
}
2022-12-05 08:52:41 +01:00
Component {
id: newMessagesMarkerComponent
NewMessagesMarker {
count: root . messageStore . newMessagesCount
timestamp: root . messageTimestamp
}
}
2020-05-27 18:59:17 -04:00
}