2022-10-07 13:50:31 +00:00
import QtQuick 2.14
import QtQuick . Layouts 1.14
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 . views . chat 1.0
import shared . controls . chat 1.0
2023-10-25 15:20:02 +00:00
import shared . stores 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
2023-05-19 16:07:50 +00:00
property var chatContentModule
2024-03-20 10:50:10 +00:00
property var chatCommunitySectionModule
2023-05-19 16:07:50 +00:00
2022-03-14 19:32:52 +00:00
property string channelEmoji
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-11-14 20:21:00 +00:00
property var stickersPopup
2022-04-13 09:59:16 +00:00
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
2023-05-19 16:07:50 +00:00
property string chatId
2021-12-09 12:53:40 +00:00
property string messageId: ""
2022-02-24 15:04:59 +00:00
property string communityId: ""
2022-10-06 13:48:04 +00:00
2021-12-09 12:53:40 +00:00
property string senderId: ""
property string senderDisplayName: ""
2022-09-14 07:35:26 +00:00
property string senderOptionalName: ""
property bool senderIsEnsVerified: false
2021-12-09 12:53:40 +00:00
property string senderIcon: ""
2023-01-10 11:29:24 +00: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 12:53:40 +00:00
property bool amISender: false
2023-01-12 09:28:45 +00:00
property bool amIChatAdmin: messageStore && messageStore . amIChatAdmin
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-07-05 10:12:27 +00:00
property string messageText: ""
2023-01-06 20:19:27 +00:00
property string unparsedText: ""
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: ""
2022-12-08 21:01:08 +00:00
property string resendError: ""
2022-10-26 08:07:46 +00:00
property int messageContentType: Constants . messageContentType . messageType
2023-03-14 00:37:22 +00:00
2021-12-09 12:53:40 +00:00
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: [ ]
2023-07-21 23:08:44 +00:00
property var linkPreviewModel
2022-09-15 07:31:38 +00:00
property string messageAttachments: ""
2022-02-09 00:04:49 +00:00
property var transactionParams
2023-12-18 22:34:10 +00:00
property var emojiReactionsModel
2021-12-09 12:53:40 +00:00
2023-08-22 15:46:26 +00:00
// These 2 properties can be dropped when the new unfurling flow supports GIFs
property var links
2023-10-18 09:03:32 +00:00
readonly property var gifLinks: {
2023-08-22 15:46:26 +00:00
if ( ! links )
return [ ]
2023-10-18 09:03:32 +00:00
const arr = links . split ( " " )
return arr . filter ( value = > value . toLowerCase ( ) . endsWith ( '.gif' ) )
2023-08-22 15:46:26 +00:00
}
2023-01-12 19:23:26 +00:00
property string responseToMessageWithId: ""
property string quotedMessageText: ""
property string quotedMessageFrom: ""
property int quotedMessageContentType: Constants . messageContentType . messageType
property int quotedMessageFromIterator: - 1
2023-01-11 21:16:35 +00:00
property bool quotedMessageDeleted: false
2023-01-10 11:29:24 +00:00
property string quotedMessageAuthorDetailsName: ""
property string quotedMessageAuthorDetailsDisplayName: ""
property string quotedMessageAuthorDetailsThumbnailImage: ""
property bool quotedMessageAuthorDetailsEnsVerified: false
property bool quotedMessageAuthorDetailsIsContact: false
property var quotedMessageAuthorDetailsColorHash
2023-01-12 19:23:26 +00:00
2023-03-06 09:47:35 +00:00
property var album: [ ]
2023-04-05 16:13:41 +00:00
property int albumCount: 0
2023-03-06 09:47:35 +00:00
2023-11-22 22:32:50 +00:00
property var quotedMessageAlbumMessageImages: [ ]
property var quotedMessageAlbumImagesCount: 0
2022-07-05 10:12:27 +00:00
// External behavior changers
property bool isInPinnedPopup: false // The pinned popup limits the number of buttons shown
2024-03-20 10:50:10 +00:00
property bool isViewMemberMessagesePopup: false // The view member messages popup limits the number of buttons
2022-07-05 10:12:27 +00:00
property bool disableHover: false // Used to force the HoverHandler to be active (useful for messages in popups)
property bool placeholderMessage: false
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
2023-01-10 11:29:24 +00:00
property int prevMessageContentType: prevMessageAsJsonObj ? prevMessageAsJsonObj.contentType : Constants . messageContentType . unknownContentType
2023-11-22 20:07:38 +00:00
property bool prevMessageDeleted: false
2023-01-10 11:29:24 +00:00
property double prevMessageTimestamp: prevMessageAsJsonObj ? prevMessageAsJsonObj.timestamp : 0
property string prevMessageSenderId: prevMessageAsJsonObj ? prevMessageAsJsonObj.senderId : ""
2021-12-09 12:53:40 +00:00
property var prevMessageAsJsonObj
property int nextMessageIndex: - 1
2023-01-20 15:36:55 +00:00
property double nextMessageTimestamp: nextMessageAsJsonObj ? nextMessageAsJsonObj.timestamp : 0
2021-12-09 12:53:40 +00:00
property var nextMessageAsJsonObj
2024-04-18 18:33:00 +00:00
readonly property bool editRestricted: root . isSticker
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
2023-11-22 20:07:38 +00:00
property bool deleted: false
property string deletedBy: ""
property string deletedByContactDisplayName: ""
property string deletedByContactIcon: ""
property string deletedByContactColorHash: ""
2023-01-10 11:29:24 +00:00
property bool shouldRepeatHeader: d . getShouldRepeatHeader ( messageTimestamp , prevMessageTimestamp , messageOutgoingStatus )
2021-10-21 22:39:53 +00:00
2021-02-01 18:40:55 +00:00
property bool hasMention: false
2021-12-09 12:53:40 +00:00
2022-10-26 08:07:46 +00:00
property bool stickersLoaded: false
2023-02-15 11:12:12 +00:00
property string sticker
2022-01-13 19:25:38 +00:00
property int stickerPack: - 1
2024-01-05 15:34:20 +00:00
property string bridgeName: ""
2022-07-05 10:12:27 +00:00
property bool isEmoji: messageContentType === Constants . messageContentType . emojiType
2022-09-15 07:31:38 +00:00
property bool isImage: messageContentType === Constants . messageContentType . imageType || ( isDiscordMessage && messageImage != "" )
2022-07-05 10:12:27 +00:00
property bool isAudio: messageContentType === Constants . messageContentType . audioType
property bool isSticker: messageContentType === Constants . messageContentType . stickerType
2022-09-15 07:31:38 +00:00
property bool isDiscordMessage: messageContentType === Constants . messageContentType . discordMessageType
2024-01-05 15:34:20 +00:00
property bool isBridgeMessage: messageContentType === Constants . messageContentType . bridgeMessageType
property bool isText: messageContentType === Constants . messageContentType . messageType || messageContentType === Constants . messageContentType . contactRequestType || isDiscordMessage || isBridgeMessage
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
2023-01-11 15:12:07 +00:00
readonly property bool isExpired: d . getIsExpired ( messageTimestamp , messageOutgoingStatus )
2022-12-08 21:01:08 +00:00
readonly property bool isSending: messageOutgoingStatus === Constants . sending && ! isExpired
2020-07-16 17:27:09 +00:00
2023-05-19 16:07:50 +00:00
function openProfileContextMenu ( sender , mouse , isReply = false ) {
if ( isReply && ! quotedMessageFrom ) {
// The responseTo message was deleted
// so we don't enable to right click the unavailable profile
2023-03-20 09:59:53 +00:00
return false
2021-12-14 14:19:55 +00:00
}
2021-12-09 12:53:40 +00:00
2023-05-19 16:07:50 +00:00
const params = {
selectedUserPublicKey: isReply ? quotedMessageFrom : root . senderId ,
selectedUserDisplayName: isReply ? quotedMessageAuthorDetailsDisplayName : root . senderDisplayName ,
selectedUserIcon: isReply ? quotedMessageAuthorDetailsThumbnailImage : root . senderIcon ,
2024-02-19 11:24:22 +00:00
isBridgedAccount: isReply ? ( quotedMessageContentType === Constants . messageContentType . bridgeMessageType ) : root . isBridgeMessage
2021-12-14 14:19:55 +00:00
}
2021-12-09 12:53:40 +00:00
2023-05-19 16:07:50 +00:00
Global . openMenu ( profileContextMenuComponent , sender , params )
}
function openMessageContextMenu ( ) {
if ( placeholderMessage || ! root . rootStore . mainModuleInst . activeSection . joined )
return
const params = {
myPublicKey: userProfile . pubKey ,
amIChatAdmin: root . amIChatAdmin ,
pinMessageAllowedForMembers: messageStore . isPinMessageAllowedForMembers ,
chatType: messageStore . chatType ,
messageId: root . messageId ,
unparsedText: root . unparsedText ,
messageSenderId: root . senderId ,
messageContentType: root . messageContentType ,
pinnedMessage: root . pinnedMessage ,
canPin: ! ! root . messageStore && root . messageStore . getNumberOfPinnedMessages ( ) < Constants . maxNumberOfPins ,
2024-04-18 18:33:00 +00:00
editRestricted: root . editRestricted ,
2023-03-20 09:59:53 +00:00
}
2023-05-19 16:07:50 +00:00
Global . openMenu ( messageContextMenuComponent , this , params )
}
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 ;
}
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-01-27 11:28:27 +00:00
function startMessageFoundAnimation ( ) {
2024-05-01 06:51:27 +00:00
if ( root . active && root . item . startMessageFoundAnimation ) {
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 )
2022-07-05 10:12:27 +00:00
z: ( typeof chatLogView === "undefined" ) ? 1 : ( chatLogView . count - index )
2022-12-05 07:52:41 +00:00
asynchronous: true
2022-07-05 10:12:27 +00:00
sourceComponent: {
2023-11-22 20:07:38 +00:00
if ( root . deleted ) {
return deletedMessageComponent
}
2022-07-05 10:12:27 +00:00
switch ( messageContentType ) {
case Constants.messageContentType.chatIdentifier:
return channelIdentifierComponent
case Constants.messageContentType.fetchMoreMessagesButton:
return fetchMoreMessagesButtonComponent
2023-06-08 12:52:03 +00:00
case Constants.messageContentType.systemMessagePrivateGroupType: // no break
2023-07-12 21:39:56 +00:00
return systemMessageGroupComponent
case Constants.messageContentType.systemMessageMutualEventSent:
case Constants.messageContentType.systemMessageMutualEventAccepted:
case Constants.messageContentType.systemMessageMutualEventRemoved:
return systemMessageMutualEventComponent
2023-05-01 16:05:16 +00:00
case Constants.messageContentType.systemMessagePinnedMessage:
2023-05-02 10:33:34 +00:00
return systemMessagePinnedMessageComponent
2022-07-05 10:12:27 +00:00
case Constants.messageContentType.gapType:
return gapComponent
2022-12-05 07:52:41 +00:00
case Constants.messageContentType.newMessagesMarker:
return newMessagesMarkerComponent
2023-05-02 14:28:11 +00:00
case Constants.messageContentType.messageType:
case Constants.messageContentType.stickerType:
case Constants.messageContentType.emojiType:
case Constants.messageContentType.transactionType:
case Constants.messageContentType.imageType:
case Constants.messageContentType.audioType:
case Constants.messageContentType.communityInviteType:
case Constants.messageContentType.discordMessageType:
2023-05-15 11:22:04 +00:00
case Constants.messageContentType.contactRequestType:
2024-01-05 15:34:20 +00:00
case Constants.messageContentType.bridgeMessageType:
2022-07-05 10:12:27 +00:00
return messageComponent
2023-05-02 14:28:11 +00:00
case Constants.messageContentType.unknownContentType:
// NOTE: We could display smth like "unknown message type, please upgrade Status to see it".
return null
default:
return null
2022-07-05 10:12:27 +00:00
}
}
QtObject {
id: d
readonly property int chatButtonSize: 32
2023-02-03 13:05:32 +00:00
property bool hideMessage: false
2022-09-20 14:14:58 +00:00
2022-07-05 10:12:27 +00:00
property string activeMessage
2023-01-06 15:43:54 +00:00
readonly property bool isMessageActive: d . activeMessage === root . messageId
2022-07-05 10:12:27 +00:00
2024-03-20 10:50:10 +00:00
readonly property bool addReactionAllowed: ! root . isInPinnedPopup &&
root . chatContentModule . chatDetails . canPostReactions &&
! root . isViewMemberMessagesePopup
2022-12-20 13:21:33 +00:00
2023-02-15 11:12:12 +00: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 13:21:33 +00: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 11:12:12 +00:00
function convertContentType ( value ) {
switch ( value ) {
2023-03-07 12:02:27 +00:00
case Constants.messageContentType.contactRequestType:
2023-02-15 11:12:12 +00: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 ;
2024-01-05 15:34:20 +00:00
case Constants.messageContentType.bridgeMessageType:
return StatusMessage . ContentType . BridgeMessage ;
2023-05-01 16:05:16 +00:00
case Constants.messageContentType.systemMessagePinnedMessage:
return StatusMessage . ContentType . SystemMessagePinnedMessage ;
2023-07-12 21:39:56 +00:00
case Constants.messageContentType.systemMessageMutualEventSent:
return StatusMessage . ContentType . SystemMessageMutualEventSent ;
case Constants.messageContentType.systemMessageMutualEventAccepted:
return StatusMessage . ContentType . SystemMessageMutualEventAccepted ;
case Constants.messageContentType.systemMessageMutualEventRemoved:
return StatusMessage . ContentType . SystemMessageMutualEventRemoved ;
2023-02-15 11:12:12 +00:00
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-09-20 14:14:58 +00:00
2023-05-19 16:07:50 +00:00
function addReactionClicked ( mouseArea , mouse ) {
if ( ! d . addReactionAllowed )
return
// Don't use mouseArea as parent, as it will be destroyed right after opening menu
const point = mouseArea . mapToItem ( root , mouse . x , mouse . y )
Global . openMenu ( addReactionContextMenu , root , { } , point )
}
2022-09-20 14:14:58 +00:00
2023-09-05 16:04:58 +00:00
function onImageClicked ( image , mouse , imageSource , url = "" ) {
2023-05-19 16:07:50 +00:00
switch ( mouse . button ) {
case Qt.LeftButton:
2024-05-15 09:36:56 +00:00
Global . openImagePopup ( image , url , false )
2023-05-19 16:07:50 +00:00
break ;
case Qt.RightButton:
2023-09-05 16:04:58 +00:00
Global . openMenu ( imageContextMenuComponent , image , { imageSource , url } )
2023-05-19 16:07:50 +00:00
break ;
}
2022-07-05 10:12:27 +00:00
}
2024-01-05 15:34:20 +00:00
function correctBridgeNameCapitalization ( bridgeName ) {
return ( bridgeName === "discord" ) ? "Discord" : bridgeName
}
2022-07-05 10:12:27 +00:00
}
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
2023-01-10 11:29:24 +00:00
nextMsgTimestamp: root . nextMessageTimestamp
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 ( )
2023-01-12 09:28:45 +00:00
chatType: root . messageStore . chatType
chatColor: root . messageStore . chatColor
2022-03-14 19:32:52 +00:00
chatEmoji: root . channelEmoji
2022-12-26 16:25:16 +00:00
amIChatAdmin: root . amIChatAdmin
2022-08-11 10:58:09 +00:00
chatIcon: {
2023-01-12 09:28:45 +00:00
if ( root . messageStore . chatType === Constants . chatType . privateGroupChat &&
root . messageStore . chatIcon !== "" ) {
return root . messageStore . chatIcon
2022-08-11 10:58:09 +00:00
}
2022-10-06 20:15:15 +00:00
return root . senderIcon
2022-08-11 10:58:09 +00:00
}
2020-06-10 18:23:18 +00:00
}
2020-05-27 22:59:17 +00:00
}
2020-07-15 21:04:14 +00:00
Component {
2023-07-12 21:39:56 +00:00
id: systemMessageGroupComponent
2023-06-08 12:52:03 +00:00
2020-07-15 21:04:14 +00:00
StyledText {
2020-06-25 20:17:42 +00:00
wrapMode: Text . Wrap
2023-06-08 12:52:03 +00:00
readonly property string systemMessageText: root . messageText . length > 0 ? root.messageText : root . unparsedText
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
` } ` +
` < / s t y l e > ` +
` < / h e a d > ` +
` < body > ` +
2023-06-08 12:52:03 +00:00
` $ { systemMessageText } ` +
2022-07-05 10:12:27 +00:00
` < / b o d y > ` +
` < / h t m l > ` ;
2020-09-21 15:47:15 +00:00
}
font.pixelSize: 14
color: Style . current . secondaryText
2023-06-08 12:52:03 +00:00
width: parent . width - 120
2020-07-15 21:04:14 +00:00
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
2023-07-12 21:39:56 +00:00
Component {
id: systemMessageMutualEventComponent
StyledText {
text: {
var displayName = root . amISender ? Utils . getContactDetailsAsJson ( chatId , false ) . displayName : root . senderDisplayName
switch ( root . messageContentType ) {
case Constants.messageContentType.systemMessageMutualEventSent:
return root . amISender ?
qsTr ( "You sent a contact request to %1" ) . arg ( displayName ) :
qsTr ( "%1 sent you a contact request" ) . arg ( displayName )
case Constants.messageContentType.systemMessageMutualEventAccepted:
return root . amISender ?
qsTr ( "You accepted %1's contact request" ) . arg ( displayName ) :
qsTr ( "%1 accepted your contact request" ) . arg ( displayName )
case Constants.messageContentType.systemMessageMutualEventRemoved:
return root . amISender ?
qsTr ( "You removed %1 as a contact" ) . arg ( displayName ) :
qsTr ( "%1 removed you as a contact" ) . arg ( displayName )
default:
return root . messageText
}
}
font.pixelSize: 14
color: Style . current . secondaryText
width: parent . width - 120
horizontalAlignment: Text . AlignHCenter
anchors.horizontalCenter: parent . horizontalCenter
textFormat: Text . RichText
topPadding: root . prevMessageIndex === 1 ? Style.current.bigPadding : 0
}
}
2023-05-02 10:33:34 +00:00
Component {
id: systemMessagePinnedMessageComponent
StatusBaseText {
width: parent . width - 120
horizontalAlignment: Text . AlignHCenter
2023-05-15 10:49:00 +00:00
text: qsTr ( "%1 pinned a message" ) . arg ( senderDisplayName )
2023-05-02 10:33:34 +00:00
color: Theme . palette . directColor3
font.family: Theme . palette . baseFont . name
font.pixelSize: Theme . primaryTextFontSize
textFormat: Text . RichText
wrapMode: Text . Wrap
topPadding: root . prevMessageIndex === 1 ? Style.current.bigPadding : 0
}
}
2023-11-22 20:07:38 +00:00
Component {
id: deletedMessageComponent
ColumnLayout {
RowLayout {
id: deletedMessage
height: 40
Layout.fillWidth: true
Layout.topMargin: 8
Layout.bottomMargin: 8
Layout.leftMargin: 16
spacing: 8
readonly property int smartIconSize: 20
readonly property int colorId: Utils . colorIdForPubkey ( root . deletedBy )
readonly property var messageDetails: StatusMessageDetails {
sender . profileImage {
width: deletedMessage . smartIconSize
height: deletedMessage . smartIconSize
assetSettings: StatusAssetSettings {
width: deletedMessage . smartIconSize
height: deletedMessage . smartIconSize
name: root . deletedByContactIcon || ""
isLetterIdenticon: name === ""
imgIsIdenticon: false
charactersLen: 1
color: Theme . palette . userCustomizationColors [ deletedMessage . colorId ]
letterSize: 14
}
name: root . deletedByContactIcon || ""
pubkey: root . deletedBy
colorId: deletedMessage . colorId
colorHash: root . deletedByContactColorHash
showRing: true
}
}
Rectangle {
Layout.preferredWidth: deletedMessage . height
Layout.preferredHeight: deletedMessage . height
radius: width / 2
color: Theme . palette . dangerColor3
Layout.alignment: Qt . AlignVCenter
StatusIcon {
anchors.centerIn: parent
width: 24
height: 24
icon: "delete"
color: Theme . palette . dangerColor1
}
}
StatusSmartIdenticon {
id: profileImage
Layout.preferredWidth: deletedMessage . smartIconSize
Layout.preferredHeight: deletedMessage . smartIconSize
Layout.alignment: Qt . AlignVCenter
visible: true
name: root . deletedByContactDisplayName
asset: deletedMessage . messageDetails . sender . profileImage . assetSettings
ringSettings: deletedMessage . messageDetails . sender . profileImage . ringSettings
}
StatusBaseText {
text: qsTr ( "<b>%1</b> deleted this message" ) . arg ( root . deletedByContactDisplayName )
Layout.alignment: Qt . AlignVCenter
}
StatusTimeStampLabel {
Layout.alignment: Qt . AlignVCenter
timestamp: root . messageTimestamp
showFullTimestamp: false
}
}
}
}
2020-07-15 21:04:14 +00:00
Component {
2022-07-05 10:12:27 +00:00
id: messageComponent
2022-10-07 13:50:31 +00:00
ColumnLayout {
spacing: 0
2022-10-26 08:07:46 +00:00
function startMessageFoundAnimation ( ) {
delegate . startMessageFoundAnimation ( ) ;
}
2022-10-07 13:50:31 +00:00
StatusDateGroupLabel {
id: dateGroupLabel
Layout.fillWidth: true
Layout.topMargin: 16
Layout.bottomMargin: 16
messageTimestamp: root . messageTimestamp
2023-01-10 11:29:24 +00:00
previousMessageTimestamp: root . prevMessageIndex === - 1 ? 0 : root . prevMessageTimestamp
2024-03-20 10:50:10 +00:00
visible: text !== "" && ! root . isInPinnedPopup && ! root . isViewMemberMessagesePopup
2021-12-14 14:19:55 +00:00
}
2022-10-07 13:50:31 +00:00
StatusMessage {
id: delegate
Layout.fillWidth: true
2023-02-15 11:12:12 +00:00
Layout.topMargin: showHeader && ! root . isInPinnedPopup ? 2 : 0
Layout.bottomMargin: ! root . isInPinnedPopup ? 2 : 0
2022-10-07 14:17:24 +00:00
2023-02-15 11:12:12 +00:00
readonly property int contentType: d . convertContentType ( root . messageContentType )
2022-12-21 15:04:25 +00:00
property string originalMessageText: ""
2023-04-28 10:28:19 +00:00
readonly property bool hideQuickActions: root . isChatBlocked ||
root . placeholderMessage ||
root . isInPinnedPopup ||
root . editModeOn ||
! root . rootStore . mainModuleInst . activeSection . joined
2022-12-21 15:04:25 +00:00
function editCancelledHandler ( ) {
root . messageStore . setEditModeOff ( root . messageId )
}
2022-10-07 13:50:31 +00:00
function editCompletedHandler ( newMessageText ) {
2022-12-21 15:04:25 +00:00
if ( delegate . originalMessageText === newMessageText ) {
delegate . editCancelledHandler ( )
return
}
2022-10-07 13:50:31 +00:00
const message = root . rootStore . plainText ( StatusQUtils . Emoji . deparse ( newMessageText ) )
2022-12-21 15:04:25 +00:00
2022-10-07 13:50:31 +00:00
if ( message . length <= 0 )
return ;
2022-01-18 21:02:47 +00:00
2022-10-07 13:50:31 +00:00
const interpretedMessage = root . messageStore . interpretMessage ( message )
root . messageStore . setEditModeOff ( root . messageId )
2022-11-08 15:44:05 +00:00
root . messageStore . editMessage ( root . messageId , root . messageContentType , interpretedMessage )
2022-10-07 13:50:31 +00:00
}
2022-07-05 10:12:27 +00:00
2022-10-07 13:50:31 +00: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 11:12:12 +00:00
isAReply: root . responseToMessageWithId !== ""
2022-10-07 13:50:31 +00:00
isEdited: root . isEdited
hasMention: root . hasMention
isPinned: root . pinnedMessage
2022-12-13 09:37:27 +00:00
pinnedBy: {
if ( ! root . pinnedMessage || root . isDiscordMessage )
return ""
const contact = Utils . getContactDetailsAsJson ( root . messagePinnedBy , false )
2023-04-19 16:48:57 +00:00
return ProfileUtils . displayName ( contact . localNickname , contact . name , contact . displayName , contact . alias )
2022-12-13 09:37:27 +00:00
}
2023-02-15 11:12:12 +00:00
isInPinnedPopup: root . isInPinnedPopup
2022-10-07 13:50:31 +00:00
hasExpired: root . isExpired
2022-12-08 21:01:08 +00:00
isSending: root . isSending
resendError: root . resendError
2022-10-07 13:50:31 +00:00
reactionsModel: root . reactionsModel
2023-07-26 14:09:36 +00:00
linkPreviewModel: root . linkPreviewModel
2023-10-18 09:03:32 +00:00
gifLinks: root . gifLinks
2022-10-07 13:50:31 +00:00
2023-01-10 11:29:24 +00:00
showHeader: root . shouldRepeatHeader || dateGroupLabel . visible || isAReply ||
2023-03-14 00:37:22 +00:00
root . prevMessageContentType === Constants . messageContentType . systemMessagePrivateGroupType ||
2023-05-15 12:19:37 +00:00
root . prevMessageContentType === Constants . messageContentType . systemMessagePinnedMessage ||
2023-07-12 21:39:56 +00:00
root . prevMessageContentType === Constants . messageContentType . systemMessageMutualEventSent ||
root . prevMessageContentType === Constants . messageContentType . systemMessageMutualEventAccepted ||
root . prevMessageContentType === Constants . messageContentType . systemMessageMutualEventRemoved ||
2024-02-27 11:03:56 +00:00
root . prevMessageContentType === Constants . messageContentType . bridgeMessageType ||
2023-11-22 20:07:38 +00:00
root . senderId !== root . prevMessageSenderId || root . prevMessageDeleted
2022-10-07 13:50:31 +00:00
isActiveMessage: d . isMessageActive
2023-02-15 11:12:12 +00:00
topPadding: showHeader ? Style.current.halfPadding : 0
bottomPadding: showHeader && d . nextMessageHasHeader ( ) ? Style.current.halfPadding : 2
2022-10-07 13:50:31 +00:00
disableHover: root . disableHover ||
2024-03-13 19:13:41 +00:00
( delegate . hideQuickActions && ! d . addReactionAllowed ) ||
2023-01-10 12:07:24 +00:00
( root . chatLogView && root . chatLogView . moving ) ||
2023-05-23 12:46:16 +00:00
Global . activityPopupOpened
2022-10-07 13:50:31 +00:00
2024-03-04 09:59:52 +00:00
disableEmojis: ! d . addReactionAllowed
2023-02-03 13:05:32 +00:00
hideMessage: d . hideMessage
2022-10-07 13:50:31 +00:00
2023-01-06 15:43:54 +00:00
overrideBackground: root . placeholderMessage
2022-10-07 13:50:31 +00:00
profileClickable: ! root . isDiscordMessage
messageAttachments: root . messageAttachments
2022-07-05 10:12:27 +00:00
2022-10-07 13:50:31 +00:00
onEditCancelled: {
2022-12-21 15:04:25 +00:00
delegate . editCancelledHandler ( )
2022-10-07 13:50:31 +00:00
}
2022-07-05 10:12:27 +00:00
2023-03-29 18:50:00 +00:00
onEditCompleted: delegate . editCompletedHandler ( newMsgText )
2022-07-05 10:12:27 +00:00
2023-05-19 16:07:50 +00:00
onImageClicked: ( image , mouse , imageSource ) = > {
d . onImageClicked ( image , mouse , imageSource )
2022-07-05 10:12:27 +00:00
}
2022-10-07 13:50:31 +00:00
onLinkActivated: {
if ( link . startsWith ( '//' ) ) {
const pubkey = link . replace ( "//" , "" ) ;
Global . openProfilePopup ( pubkey )
2022-10-20 09:05:10 +00:00
return
} else if ( link . startsWith ( '#' ) ) {
2022-10-07 13:50:31 +00:00
rootStore . chatCommunitySectionModule . switchToChannel ( link . replace ( "#" , "" ) )
2022-10-20 09:05:10 +00:00
return
2022-11-04 12:05:32 +00:00
} else if ( Utils . isStatusDeepLink ( link ) ) {
2023-10-25 15:20:02 +00:00
Global . activateDeepLink ( link )
2022-10-20 09:05:10 +00:00
return
2022-10-07 13:50:31 +00:00
}
2022-07-05 10:12:27 +00:00
2022-10-07 13:50:31 +00:00
Global . openLink ( link )
}
2022-07-05 10:12:27 +00:00
2022-10-07 13:50:31 +00:00
onProfilePictureClicked: {
2023-05-19 16:07:50 +00:00
root . openProfileContextMenu ( sender , mouse )
2022-10-07 13:50:31 +00:00
}
2022-07-05 10:12:27 +00:00
2022-10-07 13:50:31 +00:00
onReplyProfileClicked: {
2023-05-19 16:07:50 +00:00
root . openProfileContextMenu ( sender , mouse , true )
2022-10-07 13:50:31 +00:00
}
2022-07-05 10:12:27 +00:00
2022-11-08 17:34:41 +00:00
onReplyMessageClicked: {
2023-08-07 20:52:04 +00:00
root . messageStore . messageModule . jumpToMessage ( root . responseToMessageWithId )
2022-11-08 17:34:41 +00:00
}
2022-10-07 13:50:31 +00:00
onSenderNameClicked: {
2023-05-19 16:07:50 +00:00
root . openProfileContextMenu ( sender , mouse )
2022-07-05 10:12:27 +00:00
}
2022-10-07 13:50:31 +00:00
onToggleReactionClicked: {
if ( root . isChatBlocked )
return
2022-07-05 10:12:27 +00:00
2022-10-07 13:50:31 +00:00
if ( ! root . messageStore ) {
console . error ( "Reaction can not be toggled, message store is not valid" )
return
}
2022-07-05 10:12:27 +00:00
2022-10-07 13:50:31 +00:00
root . messageStore . toggleReaction ( root . messageId , emojiId )
}
2022-07-05 10:12:27 +00:00
2023-05-19 16:07:50 +00:00
onAddReactionClicked: ( sender , mouse ) = > {
d . addReactionClicked ( sender , mouse )
2022-07-05 10:12:27 +00:00
}
2022-10-07 13:50:31 +00:00
onStickerClicked: {
root . openStickerPackPopup ( root . stickerPack ) ;
}
2022-12-08 21:01:08 +00:00
onResendClicked: {
root . messageStore . resendMessage ( root . messageId )
}
2022-10-07 13:50:31 +00:00
mouseArea {
2023-01-06 15:43:54 +00:00
acceptedButtons: Qt . RightButton
2024-03-13 19:13:41 +00:00
enabled: ( ! root . isChatBlocked || d . addReactionAllowed ) &&
2023-05-19 16:07:50 +00:00
! root . placeholderMessage
2022-10-07 13:50:31 +00:00
onClicked: {
2023-05-19 16:07:50 +00:00
root . openMessageContextMenu ( )
2022-09-15 07:31:38 +00:00
}
2022-07-05 10:12:27 +00:00
}
2022-10-07 13:50:31 +00:00
messageDetails: StatusMessageDetails {
contentType: delegate . contentType
2024-01-05 15:34:20 +00:00
messageOriginInfo: {
if ( isDiscordMessage ) {
return qsTr ( "Imported from discord" )
} else if ( isBridgeMessage ) {
return qsTr ( "Bridged from %1" ) . arg ( d . correctBridgeNameCapitalization ( root . bridgeName ) )
}
return ""
}
2022-10-07 13:50:31 +00:00
messageText: root . messageText
messageContent: {
switch ( delegate . contentType )
{
case StatusMessage.ContentType.Sticker:
return root . sticker ;
case StatusMessage.ContentType.Image:
2023-03-06 09:47:35 +00:00
2022-10-07 13:50:31 +00:00
return root . messageImage ;
}
if ( root . isDiscordMessage && root . messageImage != "" ) {
2023-02-15 11:12:12 +00:00
return root . messageImage
2022-10-07 13:50:31 +00:00
}
return "" ;
}
2023-03-06 09:47:35 +00:00
album: root . album
2023-04-05 16:13:41 +00:00
albumCount: root . albumCount
2022-10-07 13:50:31 +00:00
amISender: root . amISender
sender.id: root . senderIsEnsVerified ? "" : Utils . getCompressedPk ( root . senderId )
sender.displayName: root . senderDisplayName
sender.secondaryName: root . senderOptionalName
2024-01-05 15:34:20 +00:00
sender.isEnsVerified: root . isBridgeMessage ? false : root . senderIsEnsVerified
sender.isContact: root . isBridgeMessage ? false : root . senderIsAdded
sender.trustIndicator: root . isBridgeMessage ? StatusContactVerificationIcons.TrustedType.None: root . senderTrustStatus
2022-10-07 13:50:31 +00:00
sender . profileImage {
width: 40
height: 40
name: root . senderIcon || ""
pubkey: root . senderId
colorId: Utils . colorIdForPubkey ( root . senderId )
2023-01-10 11:29:24 +00:00
colorHash: root . senderColorHash
2024-01-05 15:34:20 +00:00
showRing: ! root . isDiscordMessage && ! root . senderIsEnsVerified && ! root . isBridgeMessage
2022-10-07 13:50:31 +00:00
}
2024-01-05 15:34:20 +00:00
sender.badgeImage: Style . svg ( "discord-bridge" )
2022-07-05 10:12:27 +00:00
}
2022-10-07 13:50:31 +00:00
replyDetails: StatusMessageDetails {
2023-04-27 12:35:16 +00:00
readonly property var responseMessage: contentType === StatusMessage . ContentType . Sticker || contentType === StatusMessage . ContentType . Image
? root . messageStore . getMessageByIdAsJson ( responseToMessageWithId )
: null
onResponseMessageChanged: {
if ( ! responseMessage )
return
switch ( contentType ) {
case StatusMessage.ContentType.Sticker:
messageContent = responseMessage . sticker ;
return
case StatusMessage.ContentType.Image:
messageContent = responseMessage . messageImage ;
albumCount = responseMessage . albumImagesCount
album = responseMessage . albumMessageImages
return
default:
messageContent = ""
}
}
2023-01-11 21:16:35 +00:00
messageText: {
2023-08-07 20:52:04 +00:00
if ( messageDeleted )
2023-01-11 21:16:35 +00:00
return qsTr ( "Message deleted" )
2023-08-07 20:52:04 +00:00
if ( ! root . quotedMessageText && ! root . quotedMessageFrom )
2023-01-11 21:16:35 +00:00
return qsTr ( "Unknown message. Try fetching more messages" )
return root . quotedMessageText
}
2023-11-22 22:32:50 +00:00
album: root . quotedMessageAlbumMessageImages
albumCount: root . quotedMessageAlbumImagesCount
2023-08-07 20:52:04 +00:00
messageDeleted: root . quotedMessageDeleted
2023-02-15 11:12:12 +00:00
contentType: d . convertContentType ( root . quotedMessageContentType )
2023-01-12 19:23:26 +00:00
amISender: root . quotedMessageFrom === userProfile . pubKey
sender.id: root . quotedMessageFrom
2023-01-10 11:29:24 +00:00
sender.isContact: quotedMessageAuthorDetailsIsContact
sender.displayName: quotedMessageAuthorDetailsDisplayName
sender.isEnsVerified: quotedMessageAuthorDetailsEnsVerified
2023-01-20 15:33:21 +00:00
sender.secondaryName: quotedMessageAuthorDetailsName
2022-10-07 13:50:31 +00:00
sender . profileImage {
width: 20
height: 20
2023-01-10 11:29:24 +00:00
name: quotedMessageAuthorDetailsThumbnailImage
assetSettings.isImage: quotedMessageAuthorDetailsThumbnailImage
2024-02-19 11:24:22 +00:00
showRing: ( root . quotedMessageContentType !== Constants . messageContentType . discordMessageType ) && ! sender . isEnsVerified && ( root . quotedMessageContentType !== Constants . messageContentType . bridgeMessageType )
2023-01-12 19:23:26 +00:00
pubkey: sender . id
colorId: Utils . colorIdForPubkey ( sender . id )
2023-01-10 11:29:24 +00:00
colorHash: quotedMessageAuthorDetailsColorHash
2022-10-07 13:50:31 +00:00
}
2022-07-05 10:12:27 +00:00
}
2022-01-18 21:02:47 +00:00
2023-01-06 15:43:54 +00:00
statusChatInput: Component {
StatusChatInput {
id: editTextInput
objectName: "editMessageInput"
2022-07-05 10:12:27 +00:00
2023-01-06 15:43:54 +00:00
readonly property string messageText: editTextInput . textInput . text
2022-07-05 10:12:27 +00:00
2023-01-06 15:43:54 +00:00
// TODO: Move this property and Escape handler to StatusChatInput
property bool suggestionsOpened: false
2022-07-05 10:12:27 +00:00
2023-01-06 15:43:54 +00:00
width: parent . width
2022-07-05 10:12:27 +00:00
2023-01-06 15:43:54 +00:00
Keys.onEscapePressed: {
if ( ! suggestionsOpened ) {
delegate . editCancelled ( )
}
suggestionsOpened = false
2022-10-07 13:50:31 +00:00
}
2022-07-05 10:12:27 +00:00
2023-01-06 15:43:54 +00:00
store: root . rootStore
usersStore: root . usersStore
emojiPopup: root . emojiPopup
stickersPopup: root . stickersPopup
2022-07-05 10:12:27 +00:00
2023-01-12 09:28:45 +00:00
chatType: root . messageStore . chatType
2023-01-06 15:43:54 +00:00
isEdit: true
2022-10-07 13:50:31 +00:00
2023-03-29 18:50:00 +00:00
onSendMessage: delegate . editCompletedHandler ( editTextInput . getTextWithPublicKeys ( ) )
2022-07-05 10:12:27 +00:00
2023-01-06 15:43:54 +00:00
Component.onCompleted: {
parseMessage ( root . messageText ) ;
delegate . originalMessageText = editTextInput . textInput . text
}
2022-07-05 10:12:27 +00:00
}
}
2022-10-07 13:50:31 +00:00
linksComponent: Component {
LinksMessageView {
2023-10-09 12:41:27 +00:00
id: linksMessageView
2023-07-21 23:08:44 +00:00
linkPreviewModel: root . linkPreviewModel
2023-10-18 09:03:32 +00:00
gifLinks: root . gifLinks
2023-10-25 15:20:02 +00:00
playAnimations: root . messageStore . playAnimation
isOnline: root . rootStore . mainModuleInst . isOnline
2023-10-09 12:41:27 +00:00
highlightLink: delegate . hoveredLink
2023-09-05 16:04:58 +00:00
onImageClicked: ( image , mouse , imageSource , url ) = > {
d . onImageClicked ( image , mouse , imageSource , url )
2022-10-07 13:50:31 +00:00
}
2023-10-25 15:20:02 +00:00
onOpenContextMenu: ( item , url , domain ) = > {
Global . openMenu ( imageContextMenuComponent , item , { url: url , domain: domain , requireConfirmationOnOpen: true } )
}
2023-10-09 12:41:27 +00:00
onHoveredLinkChanged: delegate . highlightedLink = linksMessageView . hoveredLink
2023-10-25 15:20:02 +00:00
gifUnfurlingEnabled: RootStore . gifUnfurlingEnabled
canAskToUnfurlGifs: ! RootStore . neverAskAboutUnfurlingAgain
onSetNeverAskAboutUnfurlingAgain: RootStore . setNeverAskAboutUnfurlingAgain ( neverAskAgain )
2023-11-06 19:26:44 +00:00
Component.onCompleted: {
root . messageStore . messageModule . forceLinkPreviewsLocalData ( root . messageId )
}
2022-08-31 08:32:08 +00:00
}
2022-07-05 10:12:27 +00:00
}
2022-10-07 13:50:31 +00:00
transcationComponent: Component {
TransactionBubbleView {
transactionParams: root . transactionParams
store: root . rootStore
contactsStore: root . contactsStore
}
2022-07-05 10:12:27 +00:00
}
2022-10-07 13:50:31 +00:00
invitationComponent: Component {
InvitationBubbleView {
store: root . rootStore
communityId: root . communityId
}
2022-07-05 10:12:27 +00:00
}
2022-10-07 13:50:31 +00:00
quickActions: [
Loader {
2024-03-20 10:50:10 +00:00
active: d . addReactionAllowed && delegate . hovered && ! root . isViewMemberMessagesePopup
2023-01-12 09:28:45 +00:00
visible: active
2022-10-07 13:50:31 +00:00
sourceComponent: StatusFlatRoundButton {
width: d . chatButtonSize
height: d . chatButtonSize
icon.name: "reaction-b"
type: StatusFlatRoundButton . Type . Tertiary
tooltip.text: qsTr ( "Add reaction" )
2023-05-19 16:07:50 +00:00
onClicked: ( mouse ) = > {
d . addReactionClicked ( this , mouse )
2022-07-05 10:12:27 +00:00
}
}
2022-10-07 13:50:31 +00:00
} ,
Loader {
2023-04-28 10:28:19 +00:00
active: ! root . isInPinnedPopup && delegate . hovered && ! delegate . hideQuickActions
2024-03-20 10:50:10 +00:00
&& ! root . isViewMemberMessagesePopup && root . rootStore . permissionsStore . viewAndPostCriteriaMet
2023-01-12 09:28:45 +00:00
visible: active
2022-10-07 13:50:31 +00: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 )
}
2022-07-05 10:12:27 +00:00
}
2022-10-07 13:50:31 +00:00
} ,
Loader {
2024-04-18 18:33:00 +00:00
active: ! root . isInPinnedPopup && ! root . editRestricted && ! root . editModeOn && root . amISender && delegate . hovered && ! delegate . hideQuickActions
2024-03-20 10:50:10 +00:00
&& ! root . isViewMemberMessagesePopup && root . rootStore . permissionsStore . viewAndPostCriteriaMet
2022-10-07 13:50:31 +00: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 10:12:27 +00:00
}
2022-10-07 13:50:31 +00:00
}
} ,
Loader {
active: {
2023-01-12 14:32:51 +00:00
if ( ! delegate . hovered )
return false ;
2022-10-07 13:50:31 +00:00
if ( ! root . messageStore )
return false
2023-04-28 10:28:19 +00:00
if ( delegate . hideQuickActions )
return false ;
2022-07-05 10:12:27 +00:00
2024-02-06 09:23:19 +00:00
if ( ! root . rootStore . permissionsStore . viewAndPostCriteriaMet )
return false ;
2024-03-20 10:50:10 +00:00
if ( root . isViewMemberMessagesePopup ) {
return false
}
2023-01-12 09:28:45 +00:00
const chatType = root . messageStore . chatType ;
const pinMessageAllowedForMembers = root . messageStore . isPinMessageAllowedForMembers
2022-07-05 10:12:27 +00:00
2022-10-07 13:50:31 +00:00
return chatType === Constants . chatType . oneToOne ||
2022-12-26 16:25:16 +00:00
chatType === Constants . chatType . privateGroupChat && root . amIChatAdmin ||
chatType === Constants . chatType . communityChat && ( root . amIChatAdmin || pinMessageAllowedForMembers ) ;
2022-07-05 10:12:27 +00:00
}
2023-01-12 09:28:45 +00:00
visible: active
2022-10-07 13:50:31 +00: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 16:55:26 +00:00
const chatId = root . messageStore . chatType === Constants . chatType . oneToOne ? chatContentModule . getMyChatId ( ) : ""
Global . openPinnedMessagesPopupRequested ( root . rootStore , messageStore , chatContentModule . pinnedMessagesModel , root . messageId , chatId )
2022-07-05 10:12:27 +00:00
}
2022-10-07 13:50:31 +00:00
}
} ,
2023-12-12 02:16:06 +00:00
Loader {
2024-03-20 10:50:10 +00:00
active: ! root . editModeOn && delegate . hovered && ! delegate . hideQuickActions && ! root . isViewMemberMessagesePopup
2023-12-12 02:16:06 +00:00
visible: active
sourceComponent: StatusFlatRoundButton {
objectName: "markAsUnreadButton"
width: d . chatButtonSize
height: d . chatButtonSize
icon.name: "hide"
type: StatusFlatRoundButton . Type . Tertiary
tooltip.text: qsTr ( "Mark as unread" )
onClicked: {
root . messageStore . markMessageAsUnread ( root . messageId )
}
}
} ,
2022-10-07 13:50:31 +00:00
Loader {
active: {
2023-01-12 14:32:51 +00:00
if ( ! delegate . hovered )
return false ;
2022-10-07 13:50:31 +00:00
if ( root . isInPinnedPopup )
return false ;
if ( ! root . messageStore )
return false ;
2023-05-09 10:31:50 +00:00
if ( delegate . hideQuickActions )
return false ;
2024-02-06 09:23:19 +00:00
if ( ! root . rootStore . permissionsStore . viewAndPostCriteriaMet )
return false ;
2022-12-26 16:25:16 +00:00
return ( root . amISender || root . amIChatAdmin ) &&
2022-10-07 13:50:31 +00:00
( messageContentType === Constants . messageContentType . messageType ||
messageContentType === Constants . messageContentType . stickerType ||
messageContentType === Constants . messageContentType . emojiType ||
messageContentType === Constants . messageContentType . imageType ||
messageContentType === Constants . messageContentType . audioType ) ;
}
2023-01-12 09:28:45 +00:00
visible: active
2022-10-07 13:50:31 +00:00
sourceComponent: StatusFlatRoundButton {
objectName: "chatDeleteMessageButton"
width: d . chatButtonSize
height: d . chatButtonSize
icon.name: "delete"
type: StatusFlatRoundButton . Type . Tertiary
tooltip.text: qsTr ( "Delete" )
2024-03-20 10:50:10 +00:00
onClicked: root . isViewMemberMessagesePopup
? root . chatCommunitySectionModule . deleteCommunityMemberMessages ( root . senderId , root . messageId , root . chatId )
: messageStore . warnAndDeleteMessage ( root . messageId )
2022-07-05 10:12:27 +00:00
}
}
2022-10-07 13:50:31 +00:00
]
}
2022-07-05 10:12:27 +00:00
}
}
Component {
2023-05-19 16:07:50 +00:00
id: newMessagesMarkerComponent
2022-07-05 10:12:27 +00:00
2023-05-19 16:07:50 +00:00
NewMessagesMarker {
count: root . messageStore . newMessagesCount
timestamp: root . messageTimestamp
}
}
Component {
id: addReactionContextMenu
MessageAddReactionContextMenu {
2023-12-18 22:34:10 +00:00
reactionsModel: root . emojiReactionsModel
2023-05-19 16:07:50 +00:00
onToggleReaction: ( emojiId ) = > {
root . messageStore . toggleReaction ( root . messageId , emojiId )
}
onOpened: {
root . setMessageActive ( root . messageId , true )
2022-07-05 10:12:27 +00:00
}
onClosed: {
2023-05-19 16:07:50 +00:00
root . setMessageActive ( root . messageId , false )
2022-07-05 10:12:27 +00:00
destroy ( )
}
2020-07-10 15:37:23 +00:00
}
2020-05-27 22:59:17 +00:00
}
2022-12-05 07:52:41 +00:00
Component {
2023-05-19 16:07:50 +00:00
id: imageContextMenuComponent
2022-12-05 07:52:41 +00:00
2023-05-19 16:07:50 +00:00
ImageContextMenu {
onClosed: {
destroy ( )
}
}
}
Component {
id: profileContextMenuComponent
ProfileContextMenu {
store: root . rootStore
onOpenProfileClicked: ( publicKey ) = > {
Global . openProfilePopup ( publicKey , null )
}
onCreateOneToOneChat: ( communityId , chatId , ensName ) = > {
Global . changeAppSectionBySectionType ( Constants . appSection . chat )
root . rootStore . chatCommunitySectionModule . createOneToOneChat ( "" , chatId , ensName )
}
onOpened: {
root . setMessageActive ( root . messageId , true )
}
onClosed: {
root . setMessageActive ( root . messageId , false )
destroy ( )
}
2022-12-05 07:52:41 +00:00
}
}
2023-05-19 16:07:50 +00:00
Component {
id: messageContextMenuComponent
MessageContextMenuView {
store: root . rootStore
2023-12-18 22:34:10 +00:00
reactionModel: root . emojiReactionsModel
2023-05-19 16:07:50 +00:00
disabledForChat: ! root . rootStore . isUserAllowedToSendMessage
2024-03-13 19:13:41 +00:00
forceEnableEmojiReactions: ! root . rootStore . isUserAllowedToSendMessage && d . addReactionAllowed
2023-05-19 16:07:50 +00:00
onPinMessage: ( messageId ) = > {
root . messageStore . pinMessage ( messageId )
}
onUnpinMessage: ( messageId ) = > {
root . messageStore . unpinMessage ( messageId )
}
onPinnedMessagesLimitReached: ( messageId ) = > {
if ( ! root . chatContentModule ) {
console . warn ( "error on open pinned messages limit reached from message context menu - chat content module is not set" )
return
}
Global . openPinnedMessagesPopupRequested ( root . rootStore ,
root . messageStore ,
root . chatContentModule . pinnedMessagesModel ,
messageId ,
root . chatId )
}
2023-12-12 02:16:06 +00:00
onMarkMessageAsUnread: ( messageId ) = > {
root . messageStore . markMessageAsUnread ( messageId )
}
2023-05-19 16:07:50 +00:00
onToggleReaction: ( messageId , emojiId ) = > {
root . messageStore . toggleReaction ( messageId , emojiId )
}
onDeleteMessage: ( messageId ) = > {
root . messageStore . warnAndDeleteMessage ( messageId )
}
onEditClicked: ( messageId ) = > {
root . messageStore . setEditModeOn ( messageId )
}
onShowReplyArea: ( messageId , senderId ) = > {
root . showReplyArea ( messageId , senderId )
}
onOpened: {
root . setMessageActive ( model . id , true )
}
onClosed: {
root . setMessageActive ( model . id , false )
destroy ( )
}
}
}
2020-05-27 22:59:17 +00:00
}