2020-11-05 16:05:33 +03:00
/ *
Copyright ( C ) 2020 Sebastian J . Wolf and other contributors
This file is part of Fernschreiber .
Fernschreiber is free software: you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Fernschreiber is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Fernschreiber . If not , see < http: //www.gnu.org/licenses/>.
* /
import QtQuick 2.6
import Sailfish . Silica 1.0
2021-01-15 12:55:34 +03:00
import "./messageContent"
2020-11-05 16:05:33 +03:00
import "../js/twemoji.js" as Emoji
import "../js/functions.js" as Functions
2020-11-20 23:58:26 +03:00
import "../js/debug.js" as Debug
2020-11-05 16:05:33 +03:00
ListItem {
id: messageListItem
2022-05-02 00:51:03 +03:00
contentHeight: messageBackground . height + Theme . paddingMedium + ( reactionsColumn . visible ? reactionsColumn.height : 0 )
Behavior on contentHeight { NumberAnimation { duration: 200 } }
2020-12-06 07:38:03 +03:00
property var chatId
property var messageId
2021-01-08 05:14:31 +03:00
property int messageIndex
2021-01-31 23:08:39 +03:00
property int messageViewCount
2020-12-06 07:38:03 +03:00
property var myMessage
2023-12-28 15:03:13 +03:00
property var messageAlbumMessageIds
2022-05-24 00:33:17 +03:00
property var reactions
2020-12-15 21:15:12 +03:00
property bool canReplyToMessage
2022-01-07 21:18:04 +03:00
readonly property bool isAnonymous: myMessage . sender_id [ "@type" ] === "messageSenderChat"
readonly property var userInformation: tdLibWrapper . getUserInformation ( myMessage . sender_id . user_id )
2020-11-07 22:58:23 +03:00
property QtObject precalculatedValues: ListView . view . precalculatedValues
2020-11-08 22:37:17 +03:00
readonly property color textColor: isOwnMessage ? Theme.highlightColor : Theme . primaryColor
2023-07-09 14:32:21 +03:00
readonly property int textAlign: Text . AlignLeft
2020-11-08 22:37:17 +03:00
readonly property Page page: precalculatedValues . page
2020-11-15 01:50:12 +03:00
readonly property bool isSelected: messageListItem . precalculatedValues . pageIsSelecting && page . selectedMessages . some ( function ( existingMessage ) {
2020-12-06 07:38:03 +03:00
return existingMessage . id === messageId
2020-11-15 01:50:12 +03:00
} ) ;
2022-01-07 21:18:04 +03:00
readonly property bool isOwnMessage: page . myUserId === myMessage . sender_id . user_id
2021-12-05 04:25:24 +03:00
readonly property bool canDeleteMessage: myMessage . can_be_deleted_for_all_users || ( myMessage . can_be_deleted_only_for_self && myMessage . chat_id === page . myUserId )
2021-01-15 12:55:34 +03:00
property bool hasContentComponent
2021-02-16 23:36:29 +03:00
property bool additionalOptionsOpened
2023-12-03 02:44:17 +03:00
property bool wasNavigatedTo: false
2020-11-07 22:58:23 +03:00
2021-12-05 04:25:24 +03:00
readonly property var additionalItemsModel: ( extraContentLoader . item && ( "extraContextMenuItems" in extraContentLoader . item ) ) ?
extraContentLoader.item.extraContextMenuItems : 0
readonly property int numberOfExtraOptionsOtherThanDeleteMessage:
( showCopyMessageToClipboardMenuItem ? 0 : 1 ) +
( showForwardMessageMenuItem ? 0 : 1 ) +
( page . canPinMessages ( ) ? 1 : 0 ) +
( additionalItemsModel ? additionalItemsModel.length : 0 )
readonly property bool deleteMessageIsOnlyExtraOption: canDeleteMessage && ! numberOfExtraOptionsOtherThanDeleteMessage
readonly property int maxContextMenuItemCount: page . isPortrait ? 5 : 4
readonly property int baseContextMenuItemCount: ( canReplyToMessage ? 1 : 0 ) +
( myMessage . can_be_edited ? 1 : 0 ) + 2 /* "Select Message" and "More Options..." */
readonly property bool showCopyMessageToClipboardMenuItem: ( baseContextMenuItemCount + 1 ) <= maxContextMenuItemCount
readonly property bool showForwardMessageMenuItem: ( baseContextMenuItemCount + 2 ) <= maxContextMenuItemCount
// And don't count "More Options..." for "Delete Message" if "Delete Message" is the only extra option
readonly property bool haveSpaceForDeleteMessageMenuItem: ( baseContextMenuItemCount + 3 - ( deleteMessageIsOnlyExtraOption ? 1 : 0 ) ) <= maxContextMenuItemCount
2023-11-18 16:45:22 +03:00
property var chatReactions
2022-05-02 00:51:03 +03:00
property var messageReactions
2021-12-05 04:25:24 +03:00
2023-12-28 15:03:13 +03:00
highlighted: ( down || ( isSelected && messageAlbumMessageIds . length === 0 ) || additionalOptionsOpened || wasNavigatedTo ) && ! menuOpen
2020-11-15 01:50:12 +03:00
openMenuOnPressAndHold: ! messageListItem . precalculatedValues . pageIsSelecting
2020-12-15 21:15:12 +03:00
signal replyToMessage ( )
signal editMessage ( )
2021-12-05 04:25:24 +03:00
signal forwardMessage ( )
function deleteMessage ( ) {
var chatId = page . chatInformation . id
var messageId = myMessage . id
Remorse . itemAction ( messageListItem , qsTr ( "Message deleted" ) , function ( ) {
tdLibWrapper . deleteMessages ( chatId , [ messageId ] ) ;
} )
}
function copyMessageToClipboard ( ) {
Clipboard . text = Functions . getMessageText ( myMessage , true , userInformation . id , true )
}
2020-12-15 21:15:12 +03:00
2021-12-18 03:38:52 +03:00
function openContextMenu ( ) {
messageOptionsDrawer . open = false
if ( menu ) {
openMenu ( )
} else {
contextMenuLoader . active = true
}
}
2023-11-18 16:45:22 +03:00
function getInteractionText ( viewCount , reactions , size , highlightColor ) {
2022-05-24 00:33:17 +03:00
var interactionText = "" ;
if ( viewCount > 0 ) {
2023-11-18 16:45:22 +03:00
interactionText = Emoji . emojify ( "👁️ " , size ) + Functions . getShortenedCount ( viewCount ) ;
2022-05-24 00:33:17 +03:00
}
for ( var i = 0 ; i < reactions . length ; i ++ ) {
2023-11-18 16:45:22 +03:00
var reaction = reactions [ i ]
var reactionText = reaction . reaction ? reaction.reaction : ( reaction . type && reaction . type . emoji ) ? reaction.type.emoji : ""
if ( reactionText ) {
interactionText += ( " " + Emoji . emojify ( reactionText , size ) ) ;
if ( ! chatPage . isPrivateChat ) {
var count = Functions . getShortenedCount ( reaction . total_count )
interactionText += " "
interactionText += ( reaction . is_chosen ? ( "<font color='" + highlightColor + "'><b>" + count + "</b></font>" ) : count )
}
2022-05-24 00:33:17 +03:00
}
}
return interactionText ;
}
2023-12-03 01:44:14 +03:00
function openReactions ( ) {
if ( messageListItem . chatReactions ) {
Debug . log ( "Using chat reactions" )
messageListItem . messageReactions = chatReactions
showItemCompletelyTimer . requestedIndex = index ;
showItemCompletelyTimer . start ( ) ;
} else {
Debug . log ( "Obtaining message reactions" )
tdLibWrapper . getMessageAvailableReactions ( messageListItem . chatId , messageListItem . messageId ) ;
}
selectReactionBubble . visible = false ;
}
function getContentWidthMultiplier ( ) {
2023-12-03 01:50:13 +03:00
return Functions . isWidescreen ( appWindow ) ? 0.4 : 1.0
2023-12-03 01:44:14 +03:00
}
2020-11-15 01:50:12 +03:00
onClicked: {
2022-05-02 00:51:03 +03:00
if ( messageListItem . precalculatedValues . pageIsSelecting ) {
2020-11-15 01:50:12 +03:00
page . toggleMessageSelection ( myMessage ) ;
2020-12-04 06:12:00 +03:00
} else {
2021-03-27 02:17:49 +03:00
if ( messageOptionsDrawer . sourceItem !== messageListItem ) {
messageOptionsDrawer . open = false
}
2020-12-04 06:12:00 +03:00
// Allow extra context to react to click
var extraContent = extraContentLoader . item
2021-02-03 13:47:03 +03:00
if ( extraContent && extraContentLoader . contains ( mapToItem ( extraContentLoader , mouse . x , mouse . y ) ) ) {
2020-12-04 06:12:00 +03:00
extraContent . clicked ( )
2020-12-24 06:42:41 +03:00
} else if ( webPagePreviewLoader . item ) {
webPagePreviewLoader . item . clicked ( )
2020-12-04 06:12:00 +03:00
}
2022-05-02 00:51:03 +03:00
if ( messageListItem . messageReactions ) {
messageListItem . messageReactions = null ;
2023-11-30 01:47:59 +03:00
selectReactionBubble . visible = false ;
} else {
selectReactionBubble . visible = ! selectReactionBubble . visible ;
elementSelected ( index ) ;
2022-05-02 00:51:03 +03:00
}
2020-11-15 01:50:12 +03:00
}
}
2020-11-07 22:58:23 +03:00
2023-11-21 23:55:08 +03:00
onDoubleClicked: {
2023-11-27 01:36:29 +03:00
openReactions ( ) ;
2023-11-21 23:55:08 +03:00
}
2020-11-07 22:58:23 +03:00
onPressAndHold: {
2021-12-18 03:38:52 +03:00
if ( openMenuOnPressAndHold ) {
openContextMenu ( )
2020-11-15 01:50:12 +03:00
} else {
2021-12-18 03:38:52 +03:00
page . selectedMessages = [ ]
page . state = ""
2020-11-15 01:50:12 +03:00
}
2020-11-07 22:58:23 +03:00
}
2021-02-14 13:51:26 +03:00
onMenuOpenChanged: {
// When opening/closing the context menu, we no longer scroll automatically
chatView . manuallyScrolledToBottom = false ;
}
2021-02-14 23:57:48 +03:00
Connections {
2021-02-21 02:26:39 +03:00
target: additionalOptionsOpened ? messageOptionsDrawer : null
onOpenChanged: {
if ( ! messageOptionsDrawer . open ) {
additionalOptionsOpened = false
}
2021-02-14 23:57:48 +03:00
}
}
2023-11-30 01:47:59 +03:00
Connections {
target: chatPage
onResetElements: {
messageListItem . messageReactions = null ;
selectReactionBubble . visible = false ;
}
onElementSelected: {
if ( elementIndex !== index ) {
selectReactionBubble . visible = false ;
}
}
2023-12-03 02:44:17 +03:00
onNavigatedTo: {
if ( targetIndex === index ) {
messageListItem . wasNavigatedTo = true ;
restoreNormalityTimer . start ( ) ;
}
}
2023-11-30 01:47:59 +03:00
}
2020-11-07 22:58:23 +03:00
Loader {
id: contextMenuLoader
active: false
asynchronous: true
onStatusChanged: {
if ( status === Loader . Ready ) {
messageListItem . menu = item ;
messageListItem . openMenu ( ) ;
2020-11-05 16:05:33 +03:00
}
}
2020-11-07 22:58:23 +03:00
sourceComponent: Component {
ContextMenu {
MenuItem {
2021-12-05 04:25:24 +03:00
visible: canReplyToMessage
onClicked: replyToMessage ( )
2020-11-07 22:58:23 +03:00
text: qsTr ( "Reply to Message" )
}
MenuItem {
2021-12-06 00:06:05 +03:00
visible: typeof myMessage . can_be_edited !== "undefined" && myMessage . can_be_edited
2021-12-05 04:25:24 +03:00
onClicked: editMessage ( )
2020-12-15 21:15:12 +03:00
text: qsTr ( "Edit Message" )
2020-11-07 22:58:23 +03:00
}
MenuItem {
2021-12-05 04:25:24 +03:00
onClicked: page . toggleMessageSelection ( myMessage )
2021-02-14 23:57:48 +03:00
text: qsTr ( "Select Message" )
2020-11-07 22:58:23 +03:00
}
2020-11-15 01:50:12 +03:00
MenuItem {
2021-12-05 04:25:24 +03:00
visible: showCopyMessageToClipboardMenuItem
onClicked: copyMessageToClipboard ( )
text: qsTr ( "Copy Message to Clipboard" )
}
MenuItem {
visible: showForwardMessageMenuItem
onClicked: forwardMessage ( )
text: qsTr ( "Forward Message" )
}
MenuItem {
visible: canDeleteMessage && haveSpaceForDeleteMessageMenuItem
onClicked: deleteMessage ( )
text: qsTr ( "Delete Message" )
}
MenuItem {
visible: ( numberOfExtraOptionsOtherThanDeleteMessage > 0 ) ||
( deleteMessageIsOnlyExtraOption && ! haveSpaceForDeleteMessageMenuItem )
2020-11-15 01:50:12 +03:00
onClicked: {
2021-02-14 23:57:48 +03:00
messageOptionsDrawer . myMessage = myMessage ;
messageOptionsDrawer . userInformation = userInformation ;
2021-02-15 23:20:38 +03:00
messageOptionsDrawer . sourceItem = messageListItem
2021-12-05 04:25:24 +03:00
messageOptionsDrawer . additionalItemsModel = additionalItemsModel
messageOptionsDrawer . showCopyMessageToClipboardMenuItem = ! showCopyMessageToClipboardMenuItem
messageOptionsDrawer . showForwardMessageMenuItem = ! showForwardMessageMenuItem
messageOptionsDrawer . showDeleteMessageMenuItem = canDeleteMessage && ! haveSpaceForDeleteMessageMenuItem
2021-02-16 23:36:29 +03:00
messageListItem . additionalOptionsOpened = true ;
2021-02-14 23:57:48 +03:00
messageOptionsDrawer . open = true ;
2020-11-15 01:50:12 +03:00
}
2021-02-14 23:57:48 +03:00
text: qsTr ( "More Options..." )
2020-11-15 01:50:12 +03:00
}
2020-11-05 16:05:33 +03:00
}
}
}
Connections {
target: chatModel
2020-11-16 01:05:22 +03:00
onMessagesReceived: {
2023-12-28 15:03:13 +03:00
messageBackground . isUnread = messageIndex > chatModel . getLastReadMessageIndex ( ) && myMessage [ '@type' ] !== "sponsoredMessage" ;
2020-11-16 01:05:22 +03:00
}
onMessagesIncrementalUpdate: {
2023-12-28 15:03:13 +03:00
messageBackground . isUnread = messageIndex > chatModel . getLastReadMessageIndex ( ) && myMessage [ '@type' ] !== "sponsoredMessage" ;
2020-11-05 16:05:33 +03:00
}
onNewMessageReceived: {
2023-12-28 15:03:13 +03:00
messageBackground . isUnread = messageIndex > chatModel . getLastReadMessageIndex ( ) && myMessage [ '@type' ] !== "sponsoredMessage" ;
2020-11-16 01:05:22 +03:00
}
onUnreadCountUpdated: {
2023-12-28 15:03:13 +03:00
messageBackground . isUnread = messageIndex > chatModel . getLastReadMessageIndex ( ) && myMessage [ '@type' ] !== "sponsoredMessage" ;
2020-11-05 16:05:33 +03:00
}
onLastReadSentMessageUpdated: {
2023-12-28 15:03:13 +03:00
Debug . log ( "[ChatModel] Messages in this chat were read, new last read: " , lastReadSentIndex , ", updating description for index " , index , ", status: " , ( messageIndex <= lastReadSentIndex ) ) ;
messageDateText . text = getMessageStatusText ( myMessage , messageIndex , lastReadSentIndex , messageDateText . useElapsed ) ;
2020-11-05 16:05:33 +03:00
}
}
Connections {
target: tdLibWrapper
onReceivedMessage: {
2020-12-30 19:11:01 +03:00
if ( messageId === myMessage . reply_to_message_id ) {
2020-11-07 22:58:23 +03:00
messageInReplyToLoader . inReplyToMessage = message ;
2020-11-05 16:05:33 +03:00
}
}
2020-12-25 17:33:53 +03:00
onMessageNotFound: {
if ( messageId === myMessage . reply_to_message_id ) {
2021-02-10 00:35:19 +03:00
messageInReplyToLoader . inReplyToMessageDeleted = true ;
2020-12-25 17:33:53 +03:00
}
}
2022-05-01 00:51:47 +03:00
onAvailableReactionsReceived: {
2022-05-02 00:51:03 +03:00
if ( messageListItem . messageId === messageId &&
pageStack . currentPage === chatPage ) {
2022-05-01 00:51:47 +03:00
Debug . log ( "Available reactions for this message: " + reactions ) ;
2022-05-02 00:51:03 +03:00
messageListItem . messageReactions = reactions ;
2023-12-28 15:03:13 +03:00
showItemCompletelyTimer . requestedIndex = messageIndex ;
2022-05-02 00:51:03 +03:00
showItemCompletelyTimer . start ( ) ;
2022-05-29 23:10:30 +03:00
} else {
messageListItem . messageReactions = null ;
2022-05-02 00:51:03 +03:00
}
}
2023-11-30 01:47:59 +03:00
onReactionsUpdated: {
chatReactions = tdLibWrapper . getChatReactions ( page . chatInformation . id ) ;
}
2022-05-02 00:51:03 +03:00
}
Timer {
id: showItemCompletelyTimer
property int requestedIndex: ( chatView . count - 1 )
repeat: false
running: false
interval: 200
triggeredOnStart: false
onTriggered: {
2023-12-28 15:03:13 +03:00
if ( requestedIndex === messageIndex ) {
chatView . highlightMoveDuration = - 1 ;
chatView . highlightResizeDuration = - 1 ;
chatView . scrollToIndex ( requestedIndex ) ;
chatView . highlightMoveDuration = 0 ;
chatView . highlightResizeDuration = 0 ;
}
2023-11-20 23:53:57 +03:00
Debug . log ( "Show item completely timer triggered, requested index: " + requestedIndex + ", current index: " + index )
2022-05-02 00:51:03 +03:00
if ( requestedIndex === index ) {
2023-12-03 14:53:32 +03:00
var p = chatView . contentItem . mapFromItem ( reactionsColumn , 0 , 0 )
if ( chatView . contentY > p . y || p . y + reactionsColumn . height > chatView . contentY + chatView . height ) {
Debug . log ( "Moving reactions for item at" , requestedIndex , "info the view" )
chatView . highlightMoveDuration = - 1
chatView . highlightResizeDuration = - 1
chatView . scrollToIndex ( requestedIndex , height <= chatView . height ? ListView.Contain : ListView . End )
chatView . highlightMoveDuration = 0
chatView . highlightResizeDuration = 0
}
2022-05-01 00:51:47 +03:00
}
}
2020-11-05 16:05:33 +03:00
}
2023-12-03 02:44:17 +03:00
Timer {
id: restoreNormalityTimer
repeat: false
running: false
interval: 1000
triggeredOnStart: false
onTriggered: {
Debug . log ( "Restore normality for index " + index ) ;
messageListItem . wasNavigatedTo = false ;
}
}
2020-11-05 16:05:33 +03:00
Component.onCompleted: {
delegateComponentLoadingTimer . start ( ) ;
2021-12-26 05:46:52 +03:00
if ( myMessage . reply_to_message_id ) {
tdLibWrapper . getMessage ( myMessage . reply_in_chat_id ? myMessage.reply_in_chat_id : page . chatInformation . id ,
myMessage . reply_to_message_id )
2020-11-07 22:58:23 +03:00
}
2020-11-05 16:05:33 +03:00
}
2021-01-08 05:14:31 +03:00
onMyMessageChanged: {
2021-01-19 02:02:37 +03:00
Debug . log ( "[ChatModel] This message was updated, index" , messageIndex , ", updating content..." ) ;
messageDateText . text = getMessageStatusText ( myMessage , messageIndex , chatView . lastReadSentIndex , messageDateText . useElapsed ) ;
messageText . text = Emoji . emojify ( Functions . getMessageText ( myMessage , false , page . myUserId , false ) , Theme . fontSizeSmall ) ;
2021-01-08 05:14:31 +03:00
if ( webPagePreviewLoader . item ) {
2021-01-19 02:02:37 +03:00
webPagePreviewLoader . item . webPageData = myMessage . content . web_page ;
2021-01-08 05:14:31 +03:00
}
}
2020-11-05 16:05:33 +03:00
Timer {
id: delegateComponentLoadingTimer
interval: 500
repeat: false
running: false
onTriggered: {
2021-01-15 12:55:34 +03:00
if ( messageListItem . hasContentComponent ) {
var type = myMessage . content [ "@type" ] ;
2023-12-28 15:03:13 +03:00
var albumComponentPart = ( myMessage . media_album_id !== "0" && [ 'messagePhoto' , 'messageVideo' ] . indexOf ( type ) !== - 1 ) ? 'Album' : '' ;
console . log ( 'delegateComponentLoadingTimer' , myMessage . media_album_id , albumComponentPart )
2021-01-15 12:55:34 +03:00
extraContentLoader . setSource (
2023-12-28 15:03:13 +03:00
"../components/messageContent/" + type . charAt ( 0 ) . toUpperCase ( ) + type . substring ( 1 ) + albumComponentPart + ".qml" ,
2021-01-15 12:55:34 +03:00
{
messageListItem: messageListItem
} )
} else {
if ( typeof myMessage . content . web_page !== "undefined" ) { // only in messageText
webPagePreviewLoader . active = true ;
2020-11-05 16:05:33 +03:00
}
}
}
}
Row {
id: messageTextRow
spacing: Theme . paddingSmall
2020-11-07 22:58:23 +03:00
width: precalculatedValues . entryWidth
2023-12-03 01:50:13 +03:00
anchors.horizontalCenter: Functions . isWidescreen ( appWindow ) ? undefined : parent . horizontalCenter
anchors.left: Functions . isWidescreen ( appWindow ) ? parent.left : undefined
2022-05-02 00:51:03 +03:00
y: Theme . paddingSmall
2023-12-03 01:50:13 +03:00
anchors.leftMargin: Functions . isWidescreen ( appWindow ) ? Theme.paddingMedium : undefined
2020-11-05 16:05:33 +03:00
Loader {
id: profileThumbnailLoader
2020-11-07 22:58:23 +03:00
active: precalculatedValues . showUserInfo
2020-11-05 16:05:33 +03:00
asynchronous: true
2020-11-07 22:58:23 +03:00
width: precalculatedValues . profileThumbnailDimensions
height: width
2020-11-05 16:05:33 +03:00
anchors.bottom: parent . bottom
anchors.bottomMargin: Theme . paddingSmall
sourceComponent: Component {
ProfileThumbnail {
id: messagePictureThumbnail
2020-12-25 15:50:13 +03:00
photoData: messageListItem . isAnonymous ? ( ( typeof page . chatInformation . photo !== "undefined" ) ? page.chatInformation.photo.small : { } ) : ( ( typeof messageListItem . userInformation . profile_photo !== "undefined" ) ? messageListItem.userInformation.profile_photo.small : ( { } ) )
2020-11-05 16:05:33 +03:00
replacementStringHint: userText . text
2020-11-07 22:58:23 +03:00
width: Theme . itemSizeSmall
height: Theme . itemSizeSmall
visible: precalculatedValues . showUserInfo
2020-11-05 16:05:33 +03:00
MouseArea {
anchors.fill: parent
2020-12-25 15:50:13 +03:00
enabled: ! ( messageListItem . precalculatedValues . pageIsSelecting || messageListItem . isAnonymous )
2020-11-05 16:05:33 +03:00
onClicked: {
2021-01-10 04:06:41 +03:00
tdLibWrapper . createPrivateChat ( messageListItem . userInformation . id , "openDirectly" ) ;
2020-11-05 16:05:33 +03:00
}
}
}
}
}
Item {
id: messageTextItem
2020-11-07 22:58:23 +03:00
width: precalculatedValues . textItemWidth
2020-11-05 16:05:33 +03:00
height: messageBackground . height
Rectangle {
id: messageBackground
2020-11-16 01:05:22 +03:00
2020-11-05 16:05:33 +03:00
anchors {
left: parent . left
2023-07-12 01:27:18 +03:00
leftMargin: page . isPrivateChat ? ( messageListItem . isOwnMessage ? precalculatedValues.pageMarginDouble : 0 ) : 0 //левый марджин для собственных сообщений в приватных чатах. В остальных на полную ширину
2020-11-05 16:05:33 +03:00
verticalCenter: parent . verticalCenter
}
2020-11-16 17:12:18 +03:00
height: messageTextColumn . height + precalculatedValues . paddingMediumDouble
2020-11-07 22:58:23 +03:00
width: precalculatedValues . backgroundWidth
2024-05-04 18:41:19 +03:00
2023-12-28 15:03:13 +03:00
property bool isUnread: messageIndex > chatModel . getLastReadMessageIndex ( ) && myMessage [ '@type' ] !== "sponsoredMessage"
2023-07-12 01:12:53 +03:00
color: Theme . colorScheme === Theme . LightOnDark ? ( isOwnMessage ? Theme.highlightBackgroundColor : ( isUnread ? Theme.secondaryHighlightColor : Theme . secondaryColor ) ) : ( isOwnMessage ? Theme.highlightBackgroundColor : ( isUnread ? Theme.backgroundGlowColor : Theme . overlayBackgroundColor ) )
2024-05-04 18:41:19 +03:00
2020-11-05 16:05:33 +03:00
radius: parent . width / 50
2020-11-07 22:58:23 +03:00
opacity: isUnread ? 0.5 : 0.2
2021-12-08 21:48:44 +03:00
visible: appSettings . showStickersAsImages || ( myMessage . content [ '@type' ] !== "messageSticker" && myMessage . content [ '@type' ] !== "messageAnimatedEmoji" )
2020-11-05 16:05:33 +03:00
Behavior on color { ColorAnimation { duration: 200 } }
Behavior on opacity { FadeAnimation { } }
}
Column {
id: messageTextColumn
spacing: Theme . paddingSmall
2020-11-07 22:58:23 +03:00
width: precalculatedValues . textColumnWidth
2020-11-05 16:05:33 +03:00
anchors.centerIn: messageBackground
2020-11-22 02:39:49 +03:00
Label {
2020-11-05 16:05:33 +03:00
id: userText
width: parent . width
2023-12-28 15:03:13 +03:00
text: messageListItem . isOwnMessage
? qsTr ( "You" )
: Emoji . emojify ( myMessage [ '@type' ] === "sponsoredMessage"
? tdLibWrapper . getChat ( myMessage . sponsor_chat_id ) . title
: ( messageListItem . isAnonymous
? page . chatInformation . title
: Functions . getUserName ( messageListItem . userInformation ) ) , font . pixelSize )
2020-11-05 16:05:33 +03:00
font.pixelSize: Theme . fontSizeExtraSmall
font.weight: Font . ExtraBold
2020-11-07 22:58:23 +03:00
color: messageListItem . textColor
2020-11-05 16:05:33 +03:00
maximumLineCount: 1
2020-11-22 02:39:49 +03:00
truncationMode: TruncationMode . Fade
2020-11-05 16:05:33 +03:00
textFormat: Text . StyledText
2020-11-07 22:58:23 +03:00
horizontalAlignment: messageListItem . textAlign
2023-07-09 15:16:06 +03:00
visible: messageListItem . isOwnMessage ? false : ( precalculatedValues . showUserInfo || myMessage [ '@type' ] === "sponsoredMessage" )
2020-11-05 16:05:33 +03:00
MouseArea {
anchors.fill: parent
2020-12-25 15:50:13 +03:00
enabled: ! ( messageListItem . precalculatedValues . pageIsSelecting || messageListItem . isAnonymous )
2020-11-05 16:05:33 +03:00
onClicked: {
2021-01-10 04:06:41 +03:00
tdLibWrapper . createPrivateChat ( messageListItem . userInformation . id , "openDirectly" ) ;
2020-11-05 16:05:33 +03:00
}
}
}
2021-01-10 04:06:41 +03:00
MessageViaLabel {
message: myMessage
}
2020-11-07 22:58:23 +03:00
Loader {
id: messageInReplyToLoader
2021-12-06 00:06:05 +03:00
active: typeof myMessage . reply_to_message_id !== "undefined" && myMessage . reply_to_message_id !== 0
2020-11-07 22:58:23 +03:00
width: parent . width
// text height ~= 1,28*font.pixelSize
height: active ? precalculatedValues.messageInReplyToHeight : 0
property var inReplyToMessage ;
2021-02-10 00:35:19 +03:00
property bool inReplyToMessageDeleted: false ;
2020-11-07 22:58:23 +03:00
sourceComponent: Component {
2020-11-17 22:25:57 +03:00
Item {
width: messageInReplyToRow . width
height: messageInReplyToRow . height
InReplyToRow {
id: messageInReplyToRow
myUserId: page . myUserId
2021-12-18 03:38:52 +03:00
layer.enabled: messageInReplyToMouseArea . pressed && ! messageListItem . highlighted && ! messageListItem . menuOpen
layer.effect: PressEffect { source: messageInReplyToRow }
2020-11-17 22:25:57 +03:00
inReplyToMessage: messageInReplyToLoader . inReplyToMessage
2021-02-10 00:35:19 +03:00
inReplyToMessageDeleted: messageInReplyToLoader . inReplyToMessageDeleted
2020-11-17 22:25:57 +03:00
}
MouseArea {
2021-12-18 03:38:52 +03:00
id: messageInReplyToMouseArea
2020-11-17 22:25:57 +03:00
anchors.fill: parent
onClicked: {
2021-12-18 03:38:52 +03:00
if ( precalculatedValues . pageIsSelecting ) {
page . toggleMessageSelection ( myMessage )
} else {
messageOptionsDrawer . open = false
2023-12-03 02:46:47 +03:00
if ( appSettings . goToQuotedMessage ) {
chatPage . showMessage ( messageInReplyToRow . inReplyToMessage . id , true )
} else {
messageOverlayLoader . active = true
messageOverlayLoader . overlayMessage = messageInReplyToRow . inReplyToMessage
}
2021-12-18 03:38:52 +03:00
}
}
onPressAndHold: {
if ( openMenuOnPressAndHold ) {
openContextMenu ( )
}
2020-11-17 22:25:57 +03:00
}
}
2020-11-07 22:58:23 +03:00
}
}
2020-11-05 16:05:33 +03:00
}
Loader {
id: forwardedInformationLoader
2020-11-07 22:58:23 +03:00
active: typeof myMessage . forward_info !== "undefined"
2020-11-05 16:05:33 +03:00
asynchronous: true
width: parent . width
2020-11-07 22:58:23 +03:00
height: active ? ( item ? item.height : Theme . itemSizeExtraSmall ) : 0
2020-11-05 16:05:33 +03:00
sourceComponent: Component {
Row {
id: forwardedMessageInformationRow
spacing: Theme . paddingSmall
width: parent . width
Component.onCompleted: {
2023-11-18 16:45:22 +03:00
var originType = myMessage . forward_info . origin [ "@type" ]
if ( originType === "messageOriginChannel" || originType === "messageForwardOriginChannel" ) {
2020-11-07 22:58:23 +03:00
var otherChatInformation = tdLibWrapper . getChat ( myMessage . forward_info . origin . chat_id ) ;
2020-11-15 01:50:12 +03:00
forwardedThumbnail . photoData = ( typeof otherChatInformation . photo !== "undefined" ) ? otherChatInformation.photo.small : { } ;
2020-11-05 16:05:33 +03:00
forwardedChannelText . text = Emoji . emojify ( otherChatInformation . title , Theme . fontSizeExtraSmall ) ;
2023-11-18 16:45:22 +03:00
} else if ( originType === "messageOriginUser" || originType === "messageForwardOriginUser" ) {
2020-12-08 00:13:51 +03:00
var otherUserInformation = tdLibWrapper . getUserInformation ( myMessage . forward_info . origin . sender_user_id ) ;
2020-11-15 01:50:12 +03:00
forwardedThumbnail . photoData = ( typeof otherUserInformation . profile_photo !== "undefined" ) ? otherUserInformation.profile_photo.small : { } ;
2020-11-05 16:05:33 +03:00
forwardedChannelText . text = Emoji . emojify ( Functions . getUserName ( otherUserInformation ) , Theme . fontSizeExtraSmall ) ;
} else {
2020-11-14 22:02:34 +03:00
forwardedChannelText . text = Emoji . emojify ( myMessage . forward_info . origin . sender_name , Theme . fontSizeExtraSmall ) ;
forwardedThumbnail . photoData = { } ;
2020-11-05 16:05:33 +03:00
}
}
ProfileThumbnail {
id: forwardedThumbnail
replacementStringHint: forwardedChannelText . text
width: Theme . itemSizeExtraSmall
height: Theme . itemSizeExtraSmall
}
Column {
spacing: Theme . paddingSmall
2020-11-14 22:02:34 +03:00
width: parent . width - forwardedThumbnail . width - Theme . paddingSmall
2020-11-22 02:39:49 +03:00
Label {
2020-11-05 16:05:33 +03:00
font.pixelSize: Theme . fontSizeExtraSmall
width: parent . width
font.italic: true
2020-11-22 02:39:49 +03:00
truncationMode: TruncationMode . Fade
2020-11-05 16:05:33 +03:00
textFormat: Text . StyledText
text: qsTr ( "Forwarded Message" )
}
2020-11-22 02:39:49 +03:00
Label {
2020-11-05 16:05:33 +03:00
id: forwardedChannelText
font.pixelSize: Theme . fontSizeExtraSmall
color: Theme . primaryColor
width: parent . width
font.bold: true
2020-11-22 02:39:49 +03:00
truncationMode: TruncationMode . Fade
2020-11-05 16:05:33 +03:00
textFormat: Text . StyledText
text: Emoji . emojify ( forwardedMessageInformationRow . otherChatInformation . title , font . pixelSize )
}
}
}
}
}
Text {
id: messageText
width: parent . width
2021-01-19 02:02:37 +03:00
text: Emoji . emojify ( Functions . getMessageText ( myMessage , false , page . myUserId , false ) , Theme . fontSizeMedium )
2020-11-05 16:05:33 +03:00
font.pixelSize: Theme . fontSizeSmall
2020-11-07 22:58:23 +03:00
color: messageListItem . textColor
2020-11-05 16:05:33 +03:00
wrapMode: Text . Wrap
textFormat: Text . StyledText
onLinkActivated: {
2020-12-27 02:01:59 +03:00
var chatCommand = Functions . handleLink ( link ) ;
if ( chatCommand ) {
tdLibWrapper . sendTextMessage ( chatInformation . id , chatCommand ) ;
}
2020-11-05 16:05:33 +03:00
}
2020-11-07 22:58:23 +03:00
horizontalAlignment: messageListItem . textAlign
2020-11-05 16:05:33 +03:00
linkColor: Theme . highlightColor
visible: ( text !== "" )
}
2021-12-06 00:06:05 +03:00
Loader {
id: sponsoredMessageButtonLoader
active: myMessage [ '@type' ] === "sponsoredMessage"
asynchronous: true
width: parent . width
height: ( status === Loader . Ready ) ? item.implicitHeight : myMessage [ '@type' ] === "sponsoredMessage" ? Theme.itemSizeMedium : 0
sourceComponent: Component {
SponsoredMessage {
sponsoredMessageData: myMessage
width: parent . width
}
}
}
2020-11-05 16:05:33 +03:00
Loader {
id: webPagePreviewLoader
active: false
asynchronous: true
2023-12-03 01:44:14 +03:00
width: parent . width * getContentWidthMultiplier ( )
2020-12-24 06:42:41 +03:00
height: ( status === Loader . Ready ) ? item.implicitHeight : myMessage . content . web_page ? precalculatedValues.webPagePreviewHeight : 0
2020-11-05 16:05:33 +03:00
sourceComponent: Component {
WebPagePreview {
2020-11-07 22:58:23 +03:00
webPageData: myMessage . content . web_page
2020-11-05 16:05:33 +03:00
width: parent . width
2020-12-05 00:47:03 +03:00
highlighted: messageListItem . highlighted
2020-11-05 16:05:33 +03:00
}
}
}
2020-12-04 22:29:31 +03:00
2020-11-05 16:05:33 +03:00
Loader {
id: extraContentLoader
2023-12-03 01:44:14 +03:00
width: parent . width * getContentWidthMultiplier ( )
2020-11-05 16:05:33 +03:00
asynchronous: true
2023-12-28 15:03:13 +03:00
readonly property var defaultExtraContentHeight: messageListItem . hasContentComponent ? chatView . getContentComponentHeight ( model . content_type , myMessage . content , width , model . album_message_ids . length ) : 0
height: item ? item.height : defaultExtraContentHeight
2020-11-05 16:05:33 +03:00
}
2020-12-04 22:29:31 +03:00
Binding {
target: extraContentLoader . item
when: extraContentLoader . item && ( "highlighted" in extraContentLoader . item ) && ( typeof extraContentLoader . item . highlighted === "boolean" )
property: "highlighted"
value: messageListItem . highlighted
}
2020-12-27 02:01:59 +03:00
Loader {
id: replyMarkupLoader
width: parent . width
height: active ? ( myMessage . reply_markup . rows . length * ( Theme . itemSizeSmall + Theme . paddingSmall ) - Theme . paddingSmall ) : 0
asynchronous: true
active: ! ! myMessage . reply_markup && myMessage . reply_markup . rows
source: Qt . resolvedUrl ( "ReplyMarkupButtons.qml" )
}
2020-11-05 16:05:33 +03:00
Timer {
id: messageDateUpdater
interval: 60000
running: true
repeat: true
onTriggered: {
2023-12-28 15:03:13 +03:00
messageDateText . text = getMessageStatusText ( myMessage , messageIndex , chatView . lastReadSentIndex , messageDateText . useElapsed ) ;
2020-11-05 16:05:33 +03:00
}
}
Text {
width: parent . width
property bool useElapsed: true
id: messageDateText
font.pixelSize: Theme . fontSizeTiny
color: messageListItem . isOwnMessage ? Theme.secondaryHighlightColor : Theme . secondaryColor
2020-11-07 22:58:23 +03:00
horizontalAlignment: messageListItem . textAlign
2023-12-28 15:03:13 +03:00
text: getMessageStatusText ( myMessage , messageIndex , chatView . lastReadSentIndex , messageDateText . useElapsed )
2020-11-05 16:05:33 +03:00
MouseArea {
anchors.fill: parent
2020-11-15 01:50:12 +03:00
enabled: ! messageListItem . precalculatedValues . pageIsSelecting
2020-11-05 16:05:33 +03:00
onClicked: {
messageDateText . useElapsed = ! messageDateText . useElapsed ;
2023-12-28 15:03:13 +03:00
messageDateText . text = getMessageStatusText ( myMessage , messageIndex , chatView . lastReadSentIndex , messageDateText . useElapsed ) ;
2020-11-05 16:05:33 +03:00
}
}
2022-05-24 00:33:17 +03:00
}
2021-01-19 02:12:59 +03:00
2022-05-24 00:33:17 +03:00
Loader {
id: interactionLoader
width: parent . width
asynchronous: true
active: ( chatPage . isChannel && messageViewCount > 0 ) || reactions . length > 0
2022-06-09 00:13:34 +03:00
height: ( ( chatPage . isChannel && messageViewCount > 0 ) || reactions . length > 0 ) ? ( Theme . fontSizeExtraSmall + Theme . paddingSmall ) : 0
2022-05-24 00:33:17 +03:00
sourceComponent: Component {
Label {
2023-11-18 16:45:22 +03:00
text: getInteractionText ( messageViewCount , reactions , font . pixelSize , Theme . highlightColor )
2022-05-24 00:33:17 +03:00
width: parent . width
font.pixelSize: Theme . fontSizeTiny
color: messageListItem . isOwnMessage ? Theme.secondaryHighlightColor : Theme . secondaryColor
horizontalAlignment: messageListItem . textAlign
textFormat: Text . StyledText
maximumLineCount: 1
elide: Text . ElideRight
2023-11-27 01:36:29 +03:00
MouseArea {
anchors.fill: parent
onClicked: {
if ( messageListItem . messageReactions ) {
messageListItem . messageReactions = null ;
selectReactionBubble . visible = false ;
} else {
openReactions ( ) ;
}
}
}
2021-01-19 02:12:59 +03:00
}
}
2020-11-05 16:05:33 +03:00
}
}
2023-11-26 22:36:30 +03:00
Rectangle {
id: selectReactionBubble
visible: false
2023-11-26 22:53:09 +03:00
opacity: visible ? 0.5 : 0.0
Behavior on opacity { NumberAnimation { } }
2023-11-26 22:36:30 +03:00
anchors {
horizontalCenter: messageListItem . isOwnMessage ? messageBackground.left : messageBackground . right
verticalCenter: messageBackground . verticalCenter
}
height: Theme . itemSizeExtraSmall
width: Theme . itemSizeExtraSmall
color: Theme . primaryColor
radius: parent . width / 2
}
IconButton {
id: selectReactionButton
visible: selectReactionBubble . visible
2023-11-26 22:53:09 +03:00
opacity: visible ? 1.0 : 0.0
Behavior on opacity { NumberAnimation { } }
2023-11-26 22:36:30 +03:00
icon.source: "image://theme/icon-s-favorite"
anchors.centerIn: selectReactionBubble
onClicked: {
2023-11-27 01:36:29 +03:00
openReactions ( ) ;
2023-11-26 22:36:30 +03:00
}
}
2020-11-05 16:05:33 +03:00
}
}
2022-05-02 00:51:03 +03:00
Column {
id: reactionsColumn
width: parent . width - ( 2 * Theme . horizontalPageMargin )
anchors.top: messageTextRow . bottom
2023-11-26 22:36:30 +03:00
anchors.topMargin: Theme . paddingMedium
2022-05-02 00:51:03 +03:00
anchors.horizontalCenter: parent . horizontalCenter
visible: messageListItem . messageReactions ? ( messageListItem . messageReactions . length > 0 ? true : false ) : false
opacity: messageListItem . messageReactions ? ( messageListItem . messageReactions . length > 0 ? 1 : 0 ) : 0
Behavior on opacity { NumberAnimation { } }
spacing: Theme . paddingMedium
Flickable {
width: parent . width
2023-11-26 22:36:30 +03:00
height: reactionsResultRow . height + 2 * Theme . paddingMedium
2022-05-02 00:51:03 +03:00
anchors.horizontalCenter: parent . horizontalCenter
contentWidth: reactionsResultRow . width
clip: true
Row {
id: reactionsResultRow
spacing: Theme . paddingMedium
Repeater {
model: messageListItem . messageReactions
Item {
height: singleReactionRow . height
width: singleReactionRow . width
Row {
id: singleReactionRow
2023-11-26 22:36:30 +03:00
spacing: Theme . paddingMedium
2022-05-02 00:51:03 +03:00
Image {
id: emojiPicture
source: Emoji . getEmojiPath ( modelData )
2024-05-04 17:23:27 +03:00
2023-11-26 22:36:30 +03:00
width: status === Image . Ready ? Theme.fontSizeExtraLarge : 0
height: Theme . fontSizeExtraLarge
2022-05-02 00:51:03 +03:00
}
}
MouseArea {
anchors.fill: parent
onClicked: {
2023-11-26 20:05:51 +03:00
for ( var i = 0 ; i < reactions . length ; i ++ ) {
var reaction = reactions [ i ]
var reactionText = reaction . reaction ? reaction.reaction : ( reaction . type && reaction . type . emoji ) ? reaction.type.emoji : ""
if ( reactionText === modelData ) {
if ( reaction . is_chosen ) {
// Reaction is already selected
tdLibWrapper . removeMessageReaction ( chatId , messageId , reactionText )
messageReactions = null
return
}
break
}
}
// Reaction is not yet selected
tdLibWrapper . addMessageReaction ( chatId , messageId , modelData )
messageReactions = null
2023-11-26 22:36:30 +03:00
selectReactionBubble . visible = false
2022-05-02 00:51:03 +03:00
}
}
}
}
}
}
}
2020-11-05 16:05:33 +03:00
}