2020-08-21 15:26:56 +03:00
/ *
2020-10-19 20:34:47 +03:00
Copyright ( C ) 2020 Sebastian J . Wolf and other contributors
2020-08-21 15:26:56 +03:00
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/>.
* /
2020-10-31 22:49:03 +03:00
import QtQuick 2.6
2020-11-18 16:22:08 +03:00
import QtGraphicalEffects 1.0
2020-08-21 15:26:56 +03:00
import Sailfish . Silica 1.0
2020-09-27 14:49:06 +03:00
import Sailfish . Pickers 1.0
2020-09-28 00:24:22 +03:00
import Nemo . Thumbnailer 1.0
2020-08-21 15:26:56 +03:00
import WerkWolf . Fernschreiber 1.0
import "../components"
2020-11-20 23:58:26 +03:00
import "../js/debug.js" as Debug
2020-08-21 15:26:56 +03:00
import "../js/twemoji.js" as Emoji
import "../js/functions.js" as Functions
Page {
id: chatPage
allowedOrientations: Orientation . All
2020-10-16 00:43:55 +03:00
backNavigation: ! stickerPickerLoader . active
2020-08-21 15:26:56 +03:00
property bool loading: true ;
2020-08-30 00:59:29 +03:00
property bool isInitialized: false ;
2020-11-08 22:37:17 +03:00
readonly property int myUserId: tdLibWrapper . getUserInformation ( ) . id ;
2020-11-06 01:23:37 +03:00
property var chatInformation ;
2020-11-25 02:23:38 +03:00
property var secretChatDetails ;
2020-11-20 22:39:23 +03:00
property alias chatPicture: chatPictureThumbnail . photoData
2020-08-21 19:03:51 +03:00
property bool isPrivateChat: false ;
2020-11-25 02:23:38 +03:00
property bool isSecretChat: false ;
2020-11-26 02:25:15 +03:00
property bool isSecretChatReady: false ;
2020-08-21 19:03:51 +03:00
property bool isBasicGroup: false ;
property bool isSuperGroup: false ;
property bool isChannel: false ;
2021-12-09 01:57:06 +03:00
property bool isDeletedUser: false ;
2021-12-06 00:06:05 +03:00
property bool containsSponsoredMessages: false ;
2020-11-06 01:23:37 +03:00
property var chatPartnerInformation ;
2021-01-10 04:06:41 +03:00
property var botInformation ;
2020-11-06 01:23:37 +03:00
property var chatGroupInformation ;
2020-08-21 19:03:51 +03:00
property int chatOnlineMemberCount: 0 ;
2020-11-06 01:23:37 +03:00
property var emojiProposals ;
2020-11-16 18:52:48 +03:00
property bool iterativeInitialization: false ;
2021-12-11 20:29:31 +03:00
property var messageToShow ;
property string messageIdToShow ;
2023-12-03 02:46:47 +03:00
property string messageIdToScrollTo ;
2020-11-26 02:25:15 +03:00
readonly property bool userIsMember: ( ( isPrivateChat || isSecretChat ) && chatInformation [ "@type" ] ) || // should be optimized
2020-11-07 22:29:44 +03:00
( isBasicGroup || isSuperGroup ) && (
( chatGroupInformation . status [ "@type" ] === "chatMemberStatusMember" )
|| ( chatGroupInformation . status [ "@type" ] === "chatMemberStatusAdministrator" )
|| ( chatGroupInformation . status [ "@type" ] === "chatMemberStatusRestricted" && chatGroupInformation . status . is_member )
|| ( chatGroupInformation . status [ "@type" ] === "chatMemberStatusCreator" && chatGroupInformation . status . is_member )
)
2020-11-15 01:50:12 +03:00
property var selectedMessages: [ ]
2020-12-06 17:50:03 +03:00
readonly property bool isSelecting: selectedMessages . length > 0
2023-11-19 15:34:31 +03:00
readonly property bool canSendMessages: hasSendPrivilege ( "can_send_basic_messages" )
2021-01-10 04:06:41 +03:00
property bool doSendBotStartMessage
property string sendBotStartMessageParameter
2023-11-18 16:45:22 +03:00
property var availableReactions
2023-11-30 01:47:59 +03:00
signal resetElements ( )
signal elementSelected ( int elementIndex )
2023-12-03 02:44:17 +03:00
signal navigatedTo ( int targetIndex )
2020-12-15 21:15:12 +03:00
2020-11-15 01:50:12 +03:00
states: [
State {
name: "selectMessages"
2020-12-06 17:50:03 +03:00
when: isSelecting
2020-11-15 01:50:12 +03:00
PropertyChanges {
target: chatNameText
text: qsTr ( "Select Messages" )
}
PropertyChanges {
target: chatStatusText
2020-11-19 11:32:05 +03:00
text: qsTr ( "%Ln messages selected" , "number of messages selected" , chatPage . selectedMessages . length )
2020-11-15 01:50:12 +03:00
}
PropertyChanges {
target: newMessageTextField
focus: false
}
}
]
2020-12-15 21:15:12 +03:00
2020-11-15 01:50:12 +03:00
function toggleMessageSelection ( message ) {
var selectionArray = selectedMessages ;
var foundIndex = - 1
if ( selectionArray . length > 0 ) {
for ( var i = 0 ; i < selectionArray . length ; i += 1 ) {
if ( selectionArray [ i ] . id === message . id ) {
foundIndex = i ;
continue ;
}
}
}
if ( foundIndex > - 1 ) {
selectionArray . splice ( foundIndex , 1 ) ;
} else {
selectionArray . push ( message ) ;
}
selectedMessages = selectionArray ;
}
2020-08-21 19:03:51 +03:00
function updateChatPartnerStatusText ( ) {
2020-12-06 17:50:03 +03:00
if ( chatPage . isSelecting ) {
2020-11-18 17:50:23 +03:00
return
}
2020-10-19 13:20:02 +03:00
var statusText = Functions . getChatPartnerStatusText ( chatPartnerInformation . status [ '@type' ] , chatPartnerInformation . status . was_online ) ;
2020-11-25 02:23:38 +03:00
if ( chatPage . secretChatDetails ) {
2020-11-26 02:25:15 +03:00
var secretChatStatus = Functions . getSecretChatStatus ( chatPage . secretChatDetails ) ;
if ( statusText && secretChatStatus ) {
2020-11-25 02:23:38 +03:00
statusText += " - " ;
}
2020-11-26 02:25:15 +03:00
if ( secretChatStatus ) {
statusText += secretChatStatus ;
}
2020-11-25 02:23:38 +03:00
}
if ( statusText ) {
2020-10-19 13:20:02 +03:00
chatStatusText . text = statusText ;
2020-08-21 19:03:51 +03:00
}
2021-12-09 01:57:06 +03:00
if ( chatPartnerInformation . type [ '@type' ] === "userTypeDeleted" ) {
chatNameText . text = qsTr ( "Deleted User" ) ;
chatPage . isDeletedUser = true ;
}
2020-08-21 19:03:51 +03:00
}
function updateGroupStatusText ( ) {
2020-12-06 17:50:03 +03:00
if ( chatPage . isSelecting ) {
2020-11-17 18:21:20 +03:00
return
}
2020-08-21 19:03:51 +03:00
if ( chatOnlineMemberCount > 0 ) {
2020-11-18 17:50:23 +03:00
chatStatusText . text = qsTr ( "%1, %2" , "combination of '[x members], [y online]', which are separate translations" )
. arg ( qsTr ( "%1 members" , "" , chatGroupInformation . member_count )
. arg ( Functions . getShortenedCount ( chatGroupInformation . member_count ) ) )
. arg ( qsTr ( "%1 online" , "" , chatOnlineMemberCount )
. arg ( Functions . getShortenedCount ( chatOnlineMemberCount ) ) ) ;
2020-08-21 19:03:51 +03:00
} else {
if ( isChannel ) {
2020-11-18 17:50:23 +03:00
chatStatusText . text = qsTr ( "%1 subscribers" , "" , chatGroupInformation . member_count ) . arg ( Functions . getShortenedCount ( chatGroupInformation . member_count ) ) ;
2020-08-21 19:03:51 +03:00
} else {
2020-11-18 17:50:23 +03:00
chatStatusText . text = qsTr ( "%1 members" , "" , chatGroupInformation . member_count ) . arg ( Functions . getShortenedCount ( chatGroupInformation . member_count ) ) ;
2020-08-21 19:03:51 +03:00
}
}
2020-11-07 22:29:44 +03:00
joinLeaveChatMenuItem . text = chatPage . userIsMember ? qsTr ( "Leave Chat" ) : qsTr ( "Join Chat" ) ;
2020-08-21 19:03:51 +03:00
}
2020-08-21 15:26:56 +03:00
function initializePage ( ) {
2020-11-20 23:58:26 +03:00
Debug . log ( "[ChatPage] Initializing chat page..." ) ;
2020-08-29 22:39:57 +03:00
chatView . currentIndex = - 1 ;
2021-02-03 01:19:02 +03:00
chatView . lastReadSentIndex = - 1 ;
2020-08-21 19:03:51 +03:00
var chatType = chatInformation . type [ '@type' ] ;
2020-11-26 02:25:15 +03:00
isPrivateChat = chatType === "chatTypePrivate" ;
2020-11-25 02:23:38 +03:00
isSecretChat = chatType === "chatTypeSecret" ;
2020-08-21 19:03:51 +03:00
isBasicGroup = ( chatType === "chatTypeBasicGroup" ) ;
isSuperGroup = ( chatType === "chatTypeSupergroup" ) ;
2020-11-26 02:25:15 +03:00
if ( isPrivateChat || isSecretChat ) {
2020-08-21 19:03:51 +03:00
chatPartnerInformation = tdLibWrapper . getUserInformation ( chatInformation . type . user_id ) ;
updateChatPartnerStatusText ( ) ;
2020-11-25 02:23:38 +03:00
if ( isSecretChat ) {
tdLibWrapper . getSecretChat ( chatInformation . type . secret_chat_id ) ;
}
2021-01-10 04:06:41 +03:00
if ( chatPartnerInformation . type [ "@type" ] === "userTypeBot" ) {
tdLibWrapper . getUserFullInfo ( chatPartnerInformation . id )
}
2020-08-21 19:03:51 +03:00
}
2020-11-08 22:37:17 +03:00
else if ( isBasicGroup ) {
2020-08-21 19:03:51 +03:00
chatGroupInformation = tdLibWrapper . getBasicGroup ( chatInformation . type . basic_group_id ) ;
updateGroupStatusText ( ) ;
}
2020-11-08 22:37:17 +03:00
else if ( isSuperGroup ) {
2020-08-21 19:03:51 +03:00
chatGroupInformation = tdLibWrapper . getSuperGroup ( chatInformation . type . supergroup_id ) ;
isChannel = chatGroupInformation . is_channel ;
updateGroupStatusText ( ) ;
}
2020-10-17 00:00:08 +03:00
if ( stickerManager . needsReload ( ) ) {
2022-01-08 01:00:28 +03:00
Debug . log ( "[ChatPage] Recent stickers will be reloaded!" ) ;
2020-10-17 00:00:08 +03:00
tdLibWrapper . getRecentStickers ( ) ;
stickerManager . setNeedsReload ( false ) ;
}
2020-12-26 00:38:13 +03:00
tdLibWrapper . getChatPinnedMessage ( chatInformation . id ) ;
2020-12-31 02:19:36 +03:00
tdLibWrapper . toggleChatIsMarkedAsUnread ( chatInformation . id , false ) ;
2023-11-18 16:45:22 +03:00
availableReactions = tdLibWrapper . getChatReactions ( chatInformation . id ) ;
2020-08-21 15:26:56 +03:00
}
2020-11-03 01:42:23 +03:00
function getMessageStatusText ( message , listItemIndex , lastReadSentIndex , useElapsed ) {
2021-02-03 01:19:02 +03:00
Debug . log ( "Last read sent index: " + lastReadSentIndex ) ;
2020-08-31 22:51:52 +03:00
var messageStatusSuffix = "" ;
2020-10-19 13:20:02 +03:00
if ( ! message ) {
return "" ;
}
2020-09-19 21:33:51 +03:00
2021-12-06 00:06:05 +03:00
if ( message [ '@type' ] === "sponsoredMessage" ) {
return qsTr ( "Sponsored Message" ) ;
}
2020-09-19 21:33:51 +03:00
if ( message . edit_date > 0 ) {
2020-09-20 01:13:42 +03:00
messageStatusSuffix += " - " + qsTr ( "edited" ) ;
2020-09-19 21:33:51 +03:00
}
2022-01-07 21:18:04 +03:00
if ( chatPage . myUserId === message . sender_id . user_id ) {
2020-08-31 22:51:52 +03:00
messageStatusSuffix += " "
if ( listItemIndex <= lastReadSentIndex ) {
// Read by other party
messageStatusSuffix += Emoji . emojify ( "✅" , Theme . fontSizeTiny ) ;
} else {
// Not yet read by other party
if ( message . sending_state ) {
if ( message . sending_state [ '@type' ] === "messageSendingStatePending" ) {
messageStatusSuffix += Emoji . emojify ( "🕙" , Theme . fontSizeTiny ) ;
} else {
// Sending failed...
messageStatusSuffix += Emoji . emojify ( "❌" , Theme . fontSizeTiny ) ;
}
} else {
messageStatusSuffix += Emoji . emojify ( "☑️" , Theme . fontSizeTiny ) ;
}
}
}
2020-11-03 01:42:23 +03:00
return ( useElapsed ? Functions . getDateTimeElapsed ( message . date ) : Functions . getDateTimeTranslated ( message . date ) ) + messageStatusSuffix ;
2020-08-31 22:51:52 +03:00
}
2020-09-27 14:49:06 +03:00
function clearAttachmentPreviewRow ( ) {
attachmentPreviewRow . isPicture = false ;
2020-09-28 00:24:22 +03:00
attachmentPreviewRow . isVideo = false ;
attachmentPreviewRow . isDocument = false ;
2021-01-02 19:22:09 +03:00
attachmentPreviewRow . isVoiceNote = false ;
2021-01-03 03:22:30 +03:00
attachmentPreviewRow . isLocation = false ;
2021-01-10 04:06:41 +03:00
attachmentPreviewRow . fileProperties = null ;
attachmentPreviewRow . locationData = null ;
2021-01-02 19:22:09 +03:00
attachmentPreviewRow . attachmentDescription = "" ;
2021-01-03 03:22:30 +03:00
fernschreiberUtils . stopGeoLocationUpdates ( ) ;
2020-09-29 00:08:22 +03:00
}
2020-09-29 22:00:23 +03:00
function controlSendButton ( ) {
2020-09-29 00:08:22 +03:00
if ( newMessageTextField . text . length !== 0
|| attachmentPreviewRow . isPicture
|| attachmentPreviewRow . isDocument
2021-01-02 19:22:09 +03:00
|| attachmentPreviewRow . isVideo
2021-01-03 03:22:30 +03:00
|| attachmentPreviewRow . isVoiceNote
|| attachmentPreviewRow . isLocation ) {
2020-09-29 00:08:22 +03:00
newMessageSendButton . enabled = true ;
} else {
newMessageSendButton . enabled = false ;
}
2020-09-27 14:49:06 +03:00
}
function sendMessage ( ) {
if ( newMessageColumn . editMessageId !== "0" ) {
tdLibWrapper . editMessageText ( chatInformation . id , newMessageColumn . editMessageId , newMessageTextField . text ) ;
} else {
if ( attachmentPreviewRow . visible ) {
if ( attachmentPreviewRow . isPicture ) {
2020-09-29 00:08:22 +03:00
tdLibWrapper . sendPhotoMessage ( chatInformation . id , attachmentPreviewRow . fileProperties . filePath , newMessageTextField . text , newMessageColumn . replyToMessageId ) ;
2020-09-27 14:49:06 +03:00
}
2020-09-28 00:24:22 +03:00
if ( attachmentPreviewRow . isVideo ) {
2020-09-29 00:08:22 +03:00
tdLibWrapper . sendVideoMessage ( chatInformation . id , attachmentPreviewRow . fileProperties . filePath , newMessageTextField . text , newMessageColumn . replyToMessageId ) ;
2020-09-28 00:24:22 +03:00
}
if ( attachmentPreviewRow . isDocument ) {
2020-09-29 00:08:22 +03:00
tdLibWrapper . sendDocumentMessage ( chatInformation . id , attachmentPreviewRow . fileProperties . filePath , newMessageTextField . text , newMessageColumn . replyToMessageId ) ;
2020-09-28 00:24:22 +03:00
}
2021-01-02 19:22:09 +03:00
if ( attachmentPreviewRow . isVoiceNote ) {
tdLibWrapper . sendVoiceNoteMessage ( chatInformation . id , fernschreiberUtils . voiceNotePath ( ) , newMessageTextField . text , newMessageColumn . replyToMessageId ) ;
}
2021-01-03 03:22:30 +03:00
if ( attachmentPreviewRow . isLocation ) {
tdLibWrapper . sendLocationMessage ( chatInformation . id , attachmentPreviewRow . locationData . latitude , attachmentPreviewRow . locationData . longitude , attachmentPreviewRow . locationData . horizontalAccuracy , newMessageColumn . replyToMessageId ) ;
}
2020-09-27 14:49:06 +03:00
clearAttachmentPreviewRow ( ) ;
} else {
tdLibWrapper . sendTextMessage ( chatInformation . id , newMessageTextField . text , newMessageColumn . replyToMessageId ) ;
}
2021-01-01 03:30:23 +03:00
if ( appSettings . focusTextAreaAfterSend ) {
lostFocusTimer . start ( ) ;
}
2020-09-27 14:49:06 +03:00
}
2020-09-29 22:00:23 +03:00
controlSendButton ( ) ;
2020-10-18 22:54:15 +03:00
newMessageInReplyToRow . inReplyToMessage = null ;
newMessageColumn . editMessageId = "0" ;
2021-01-03 03:22:30 +03:00
fernschreiberUtils . stopGeoLocationUpdates ( ) ;
2020-09-27 14:49:06 +03:00
}
2020-10-18 19:57:01 +03:00
function getWordBoundaries ( text , cursorPosition ) {
var wordBoundaries = { beginIndex : 0 , endIndex : text . length } ;
var currentIndex = 0 ;
for ( currentIndex = ( cursorPosition - 1 ) ; currentIndex > 0 ; currentIndex -- ) {
if ( text . charAt ( currentIndex ) === ' ' ) {
wordBoundaries . beginIndex = currentIndex + 1 ;
break ;
}
}
for ( currentIndex = cursorPosition ; currentIndex < text . length ; currentIndex ++ ) {
if ( text . charAt ( currentIndex ) === ' ' ) {
wordBoundaries . endIndex = currentIndex ;
break ;
}
}
return wordBoundaries ;
}
function handleMessageTextReplacement ( text , cursorPosition ) {
2021-01-10 04:06:41 +03:00
if ( ! newMessageTextField . focus ) {
return ;
}
2020-10-18 19:57:01 +03:00
var wordBoundaries = getWordBoundaries ( text , cursorPosition ) ;
var currentWord = text . substring ( wordBoundaries . beginIndex , wordBoundaries . endIndex ) ;
if ( currentWord . length > 1 && currentWord . charAt ( 0 ) === ':' ) {
tdLibWrapper . searchEmoji ( currentWord . substring ( 1 ) ) ;
} else {
chatPage . emojiProposals = null ;
}
2020-11-29 01:00:10 +03:00
if ( currentWord . length > 1 && currentWord . charAt ( 0 ) === '@' ) {
knownUsersRepeater . model = knownUsersProxyModel ;
knownUsersProxyModel . setFilterWildcard ( "*" + currentWord . substring ( 1 ) + "*" ) ;
} else {
knownUsersRepeater . model = undefined ;
}
2021-01-10 04:06:41 +03:00
2020-10-18 19:57:01 +03:00
}
function replaceMessageText ( text , cursorPosition , newText ) {
var wordBoundaries = getWordBoundaries ( text , cursorPosition ) ;
var newCompleteText = text . substring ( 0 , wordBoundaries . beginIndex ) + newText + " " + text . substring ( wordBoundaries . endIndex ) ;
var newIndex = wordBoundaries . beginIndex + newText . length + 1 ;
newMessageTextField . text = newCompleteText ;
newMessageTextField . cursorPosition = newIndex ;
lostFocusTimer . start ( ) ;
}
2021-01-10 04:06:41 +03:00
function setMessageText ( text , doSend ) {
if ( doSend ) {
tdLibWrapper . sendTextMessage ( chatInformation . id , text , "0" ) ;
}
else {
newMessageTextField . text = text
newMessageTextField . cursorPosition = text . length
lostFocusTimer . start ( ) ;
}
}
2021-02-18 01:48:08 +03:00
function startForwardingMessages ( messages ) {
var ids = Functions . getMessagesArrayIds ( messages ) ;
var neededPermissions = Functions . getMessagesNeededForwardPermissions ( messages ) ;
var chatId = chatInformation . id ;
pageStack . push ( Qt . resolvedUrl ( "../pages/ChatSelectionPage.qml" ) , {
myUserId: chatPage . myUserId ,
headerDescription: qsTr ( "Forward %Ln messages" , "dialog header" , ids . length ) ,
payload: { fromChatId: chatId , messageIds: ids , neededPermissions: neededPermissions } ,
state: "forwardMessages"
} ) ;
}
2020-11-15 01:50:12 +03:00
function forwardMessages ( fromChatId , messageIds ) {
forwardMessagesTimer . fromChatId = fromChatId ;
forwardMessagesTimer . messageIds = messageIds ;
forwardMessagesTimer . start ( ) ;
}
2020-11-16 12:28:16 +03:00
function hasSendPrivilege ( privilege ) {
2020-11-18 13:13:05 +03:00
var groupStatus = chatGroupInformation ? chatGroupInformation.status : null
var groupStatusType = groupStatus ? groupStatus [ "@type" ] : null
2020-11-17 13:14:36 +03:00
return chatPage . isPrivateChat
|| ( groupStatusType === "chatMemberStatusMember" && chatInformation . permissions [ privilege ] )
|| groupStatusType === "chatMemberStatusAdministrator"
|| groupStatusType === "chatMemberStatusCreator"
|| ( groupStatusType === "chatMemberStatusRestricted" && groupStatus . permissions [ privilege ] )
2020-11-26 02:25:15 +03:00
|| ( chatPage . isSecretChat && chatPage . isSecretChatReady )
2020-11-16 12:28:16 +03:00
}
2020-11-18 16:22:08 +03:00
function canPinMessages ( ) {
2020-11-20 23:58:26 +03:00
Debug . log ( "Can we pin messages?" ) ;
2020-11-26 02:25:15 +03:00
if ( chatPage . isPrivateChat || chatPage . isSecretChat ) {
Debug . log ( "Private/Secret Chat: No!" ) ;
2020-11-18 16:22:08 +03:00
return false ;
}
if ( chatPage . chatGroupInformation . status [ "@type" ] === "chatMemberStatusCreator" ) {
2020-11-20 23:58:26 +03:00
Debug . log ( "Creator of this chat: Yes!" ) ;
2020-11-18 16:22:08 +03:00
return true ;
}
if ( chatPage . chatInformation . permissions . can_pin_messages ) {
2020-11-20 23:58:26 +03:00
Debug . log ( "All people can pin: Yes!" ) ;
2020-11-18 16:22:08 +03:00
return true ;
}
if ( chatPage . chatGroupInformation . status [ "@type" ] === "chatMemberStatusAdministrator" ) {
2020-11-20 23:58:26 +03:00
Debug . log ( "Admin with privileges? " , chatPage . chatGroupInformation . status . can_pin_messages ) ;
2020-11-18 16:22:08 +03:00
return chatPage . chatGroupInformation . status . can_pin_messages ;
}
if ( chatPage . chatGroupInformation . status [ "@type" ] === "chatMemberStatusRestricted" ) {
2020-11-20 23:58:26 +03:00
Debug . log ( "Restricted, but can pin messages? " , chatPage . chatGroupInformation . status . permissions . can_pin_messages ) ;
2020-11-18 16:22:08 +03:00
return chatPage . chatGroupInformation . status . permissions . can_pin_messages ;
}
2020-11-20 23:58:26 +03:00
Debug . log ( "Something else: No!" ) ;
2020-11-18 16:22:08 +03:00
return false ;
}
2020-11-16 12:28:16 +03:00
2020-12-27 02:16:25 +03:00
function resetFocus ( ) {
if ( searchInChatField . text === "" ) {
chatOverviewItem . visible = true ;
}
searchInChatField . focus = false ;
chatPage . focus = true ;
}
2023-12-03 02:46:47 +03:00
function showMessage ( messageId , initialRun ) {
// Means we tapped a quoted message and had to load it.
if ( initialRun ) {
chatPage . messageIdToScrollTo = messageId
}
if ( chatPage . messageIdToScrollTo && chatPage . messageIdToScrollTo != "" ) {
2024-05-14 18:00:32 +03:00
var index = chatModel . getDisplayedMessageIndex ( chatPage . messageIdToScrollTo ) ;
2023-12-03 02:46:47 +03:00
if ( index !== - 1 ) {
chatPage . messageIdToScrollTo = "" ;
chatView . scrollToIndex ( index ) ;
2023-12-03 02:44:17 +03:00
navigatedTo ( index ) ;
2023-12-03 02:46:47 +03:00
} else if ( initialRun ) {
// we only want to do this once.
chatModel . triggerLoadHistoryForMessage ( chatPage . messageIdToScrollTo )
}
}
}
2020-11-15 01:50:12 +03:00
Timer {
id: forwardMessagesTimer
interval: 200
property string fromChatId
property var messageIds
onTriggered: {
if ( chatPage . loading ) {
forwardMessagesTimer . start ( )
} else {
var forwardedToSecretChat = chatInformation . type [ "@type" ] === "chatTypeSecret" ;
tdLibWrapper . forwardMessages ( chatInformation . id , fromChatId , messageIds , forwardedToSecretChat , false ) ;
}
}
}
2020-10-18 19:57:01 +03:00
2020-12-27 02:16:25 +03:00
Timer {
id: searchInChatTimer
interval: 300
running: false
repeat: false
onTriggered: {
Debug . log ( "Searching for '" + searchInChatField . text + "'" ) ;
chatModel . setSearchQuery ( searchInChatField . text ) ;
}
}
2020-08-21 15:26:56 +03:00
Component.onCompleted: {
initializePage ( ) ;
}
2020-10-19 13:20:02 +03:00
Component.onDestruction: {
2021-12-09 01:57:06 +03:00
if ( chatPage . canSendMessages && ! chatPage . isDeletedUser ) {
2023-11-19 00:14:59 +03:00
tdLibWrapper . setChatDraftMessage ( chatInformation . id , 0 , newMessageColumn . replyToMessageId , newMessageTextField . text ,
newMessageInReplyToRow . inReplyToMessage ? newMessageInReplyToRow.inReplyToMessage.id : 0 ) ;
2020-12-31 21:27:44 +03:00
}
2021-01-03 03:22:30 +03:00
fernschreiberUtils . stopGeoLocationUpdates ( ) ;
2020-10-19 13:20:02 +03:00
tdLibWrapper . closeChat ( chatInformation . id ) ;
}
2020-08-21 15:47:08 +03:00
onStatusChanged: {
2020-10-19 13:20:02 +03:00
switch ( status ) {
case PageStatus.Activating:
2020-08-25 00:02:08 +03:00
tdLibWrapper . openChat ( chatInformation . id ) ;
2021-01-10 04:06:41 +03:00
if ( ! chatPage . isInitialized ) {
if ( chatInformation . draft_message ) {
if ( chatInformation . draft_message && chatInformation . draft_message . input_message_text ) {
newMessageTextField . text = chatInformation . draft_message . input_message_text . text . text ;
if ( chatInformation . draft_message . reply_to_message_id ) {
tdLibWrapper . getMessage ( chatInformation . id , chatInformation . draft_message . reply_to_message_id ) ;
}
}
}
}
2021-03-27 02:17:49 +03:00
break ;
case PageStatus.Deactivating:
messageOptionsDrawer . open = false
2020-10-19 13:20:02 +03:00
break ;
case PageStatus.Active:
2020-08-30 00:59:29 +03:00
if ( ! chatPage . isInitialized ) {
chatModel . initialize ( chatInformation ) ;
2020-11-16 23:38:55 +03:00
2020-11-17 12:09:06 +03:00
pageStack . pushAttached ( Qt . resolvedUrl ( "ChatInformationPage.qml" ) , { "chatInformation" : chatInformation , "privateChatUserInformation" : chatPartnerInformation , "groupInformation" : chatGroupInformation , "chatOnlineMemberCount" : chatOnlineMemberCount } ) ;
2020-12-31 02:59:05 +03:00
2021-01-10 04:06:41 +03:00
if ( doSendBotStartMessage ) {
tdLibWrapper . sendBotStartMessage ( chatInformation . id , chatInformation . id , sendBotStartMessageParameter , "" )
2020-12-31 02:59:05 +03:00
}
2020-08-30 00:59:29 +03:00
}
2020-10-19 13:20:02 +03:00
break ;
2020-11-20 22:39:23 +03:00
case PageStatus.Inactive:
2020-11-21 12:12:59 +03:00
if ( pageStack . depth === 1 ) {
// Only clear chat model if navigated back to overview page. In other cases we keep the information...
chatModel . clear ( ) ;
2023-11-30 01:47:59 +03:00
} else {
resetElements ( ) ;
2020-11-21 12:12:59 +03:00
}
2023-11-30 01:47:59 +03:00
2020-11-20 22:39:23 +03:00
break ;
2020-08-29 19:04:23 +03:00
}
2020-08-21 15:47:08 +03:00
}
2020-08-21 19:03:51 +03:00
Connections {
target: tdLibWrapper
onUserUpdated: {
2020-11-26 02:25:15 +03:00
if ( ( isPrivateChat || isSecretChat ) && chatPartnerInformation . id . toString ( ) === userId ) {
2020-08-21 19:03:51 +03:00
chatPartnerInformation = userInformation ;
updateChatPartnerStatusText ( ) ;
}
}
onBasicGroupUpdated: {
if ( isBasicGroup && chatGroupInformation . id . toString ( ) === groupId ) {
chatGroupInformation = groupInformation ;
updateGroupStatusText ( ) ;
}
}
onSuperGroupUpdated: {
if ( isSuperGroup && chatGroupInformation . id . toString ( ) === groupId ) {
chatGroupInformation = groupInformation ;
updateGroupStatusText ( ) ;
}
}
onChatOnlineMemberCountUpdated: {
2020-11-20 23:58:26 +03:00
Debug . log ( isSuperGroup , "/" , isBasicGroup , "/" , chatInformation . id . toString ( ) , "/" , chatId ) ;
2020-08-21 19:03:51 +03:00
if ( ( isSuperGroup || isBasicGroup ) && chatInformation . id . toString ( ) === chatId ) {
chatOnlineMemberCount = onlineMemberCount ;
updateGroupStatusText ( ) ;
}
}
2020-09-30 00:37:56 +03:00
onFileUpdated: {
uploadStatusRow . visible = fileInformation . remote . is_uploading_active ;
2020-11-14 23:01:52 +03:00
if ( uploadStatusRow . visible ) {
uploadingProgressBar . maximumValue = fileInformation . size ;
uploadingProgressBar . value = fileInformation . remote . uploaded_size ;
}
2020-09-30 00:37:56 +03:00
}
2020-10-18 19:57:01 +03:00
onEmojiSearchSuccessful: {
chatPage . emojiProposals = result ;
}
2020-11-16 01:29:04 +03:00
onErrorReceived: {
2020-11-16 16:37:14 +03:00
Functions . handleErrorMessage ( code , message ) ;
2020-11-16 01:29:04 +03:00
}
2020-11-16 23:39:11 +03:00
onReceivedMessage: {
2020-12-26 00:38:13 +03:00
if ( message . is_pinned ) {
2020-11-20 23:58:26 +03:00
Debug . log ( "[ChatPage] Received pinned message" ) ;
2020-11-16 23:39:11 +03:00
pinnedMessageItem . pinnedMessage = message ;
}
2021-01-01 23:06:38 +03:00
if ( chatInformation . draft_message && messageId === chatInformation . draft_message . reply_to_message_id ) {
2020-12-31 02:59:05 +03:00
newMessageInReplyToRow . inReplyToMessage = message ;
}
2021-12-11 20:29:31 +03:00
Debug . log ( "Received message ID: " + messageId + ", message ID to show: " + chatPage . messageIdToShow )
if ( chatPage . messageIdToShow && chatPage . messageIdToShow === String ( messageId ) ) {
messageOverlayLoader . overlayMessage = message ;
messageOverlayLoader . active = true ;
}
2020-11-16 23:39:11 +03:00
}
2020-11-25 02:23:38 +03:00
onSecretChatReceived: {
2020-11-28 21:11:51 +03:00
if ( secretChatId === chatInformation . type . secret_chat_id ) {
2020-11-25 02:23:38 +03:00
Debug . log ( "[ChatPage] Received detailed information about this secret chat" ) ;
chatPage . secretChatDetails = secretChat ;
updateChatPartnerStatusText ( ) ;
2020-11-26 02:25:15 +03:00
chatPage . isSecretChatReady = chatPage . secretChatDetails . state [ "@type" ] === "secretChatStateReady" ;
2020-11-25 02:23:38 +03:00
}
}
onSecretChatUpdated: {
2020-11-27 21:42:39 +03:00
if ( secretChatId . toString ( ) === chatInformation . type . secret_chat_id . toString ( ) ) {
2020-11-25 02:23:38 +03:00
Debug . log ( "[ChatPage] Detailed information about this secret chat was updated" ) ;
chatPage . secretChatDetails = secretChat ;
updateChatPartnerStatusText ( ) ;
2020-11-26 02:25:15 +03:00
chatPage . isSecretChatReady = chatPage . secretChatDetails . state [ "@type" ] === "secretChatStateReady" ;
2020-11-25 02:23:38 +03:00
}
}
2021-01-10 04:06:41 +03:00
onCallbackQueryAnswer: {
if ( text . length > 0 ) { // ignore bool "alert", just show as notification:
appNotification . show ( Emoji . emojify ( text , Theme . fontSizeSmall ) ) ;
}
if ( url . length > 0 ) {
Functions . handleLink ( url ) ;
}
}
onUserFullInfoReceived: {
2023-11-18 17:35:02 +03:00
if ( ( isPrivateChat || isSecretChat ) && userFullInfo [ "@extra" ] === chatPartnerInformation . id . toString ( ) ) {
2021-01-10 04:06:41 +03:00
chatPage . botInformation = userFullInfo ;
}
}
onUserFullInfoUpdated: {
2023-11-18 17:35:02 +03:00
if ( ( isPrivateChat || isSecretChat ) && userId === chatPartnerInformation . id ) {
2021-01-10 04:06:41 +03:00
chatPage . botInformation = userFullInfo ;
}
}
2022-01-08 00:03:58 +03:00
onSponsoredMessageReceived: {
2021-12-06 00:06:05 +03:00
chatPage . containsSponsoredMessages = true ;
}
2023-11-27 01:36:29 +03:00
onReactionsUpdated: {
availableReactions = tdLibWrapper . getChatReactions ( chatInformation . id ) ;
}
2020-08-21 19:03:51 +03:00
}
2020-08-22 22:43:20 +03:00
Connections {
target: chatModel
onMessagesReceived: {
2023-12-28 15:03:13 +03:00
var proxyIndex = chatProxyModel . mapRowFromSource ( modelIndex , - 1 ) ;
Debug . log ( "[ChatPage] Messages received, view has " , chatView . count , " messages, last known message index " , proxyIndex , "(" + modelIndex + "), own messages were read before index " , lastReadSentIndex ) ;
2020-11-16 18:52:48 +03:00
if ( totalCount === 0 ) {
if ( chatPage . iterativeInitialization ) {
chatPage . iterativeInitialization = false ;
2020-11-20 23:58:26 +03:00
Debug . log ( "[ChatPage] actually, skipping that: No Messages in Chat." ) ;
2020-11-16 18:52:48 +03:00
chatView . positionViewAtEnd ( ) ;
chatPage . loading = false ;
return ;
} else {
chatPage . iterativeInitialization = true ;
}
2020-10-19 13:20:02 +03:00
}
2020-08-30 20:04:16 +03:00
chatView . lastReadSentIndex = lastReadSentIndex ;
2023-12-28 15:03:13 +03:00
chatView . scrollToIndex ( proxyIndex ) ;
2020-09-14 00:25:48 +03:00
chatPage . loading = false ;
2023-12-28 15:03:13 +03:00
if ( chatOverviewItem . visible && proxyIndex >= ( chatView . count - 10 ) ) {
2020-11-16 01:05:22 +03:00
chatView . inCooldown = true ;
chatModel . triggerLoadMoreFuture ( ) ;
}
2020-09-16 22:12:39 +03:00
if ( chatView . height > chatView . contentHeight ) {
2020-11-20 23:58:26 +03:00
Debug . log ( "[ChatPage] Chat content quite small..." ) ;
2020-11-04 14:26:57 +03:00
viewMessageTimer . queueViewMessage ( chatView . count - 1 ) ;
2020-09-16 22:12:39 +03:00
}
2020-12-28 19:12:21 +03:00
chatViewCooldownTimer . restart ( ) ;
2021-02-16 23:48:14 +03:00
chatViewStartupReadTimer . restart ( ) ;
2023-11-23 00:53:17 +03:00
2023-11-26 22:36:30 +03:00
/ *
// Double-tap for reactions is currently disabled, let's see if we'll ever need it again
2023-11-23 00:53:17 +03:00
var remainingDoubleTapHints = appSettings . remainingDoubleTapHints ;
Debug . log ( "Remaining double tap hints: " + remainingDoubleTapHints ) ;
if ( remainingDoubleTapHints > 0 ) {
doubleTapHintTimer . start ( ) ;
tapHint . visible = true ;
tapHintLabel . visible = true ;
appSettings . remainingDoubleTapHints = remainingDoubleTapHints - 1 ;
}
2023-11-26 22:36:30 +03:00
* /
2020-08-22 22:43:20 +03:00
}
2020-08-23 00:49:02 +03:00
onNewMessageReceived: {
2022-01-07 21:18:04 +03:00
if ( ( chatView . manuallyScrolledToBottom && Qt . application . state === Qt . ApplicationActive ) || message . sender_id . user_id === chatPage . myUserId ) {
2020-11-20 23:58:26 +03:00
Debug . log ( "[ChatPage] Own message received or was scrolled to bottom, scrolling down to see it..." ) ;
2020-11-02 22:54:18 +03:00
chatView . scrollToIndex ( chatView . count - 1 ) ;
2021-02-05 23:57:22 +03:00
viewMessageTimer . queueViewMessage ( chatView . count - 1 ) ;
2020-09-22 21:32:35 +03:00
}
2020-08-23 00:49:02 +03:00
}
2020-08-29 22:39:57 +03:00
onUnreadCountUpdated: {
2020-11-20 23:58:26 +03:00
Debug . log ( "[ChatPage] Unread count updated, new count: " , unreadCount ) ;
2020-08-29 22:39:57 +03:00
chatInformation . unread_count = unreadCount ;
2023-02-05 23:44:02 +03:00
chatUnreadMessagesItem . visible = ( ! chatPage . loading && unreadCount > 0 && chatOverviewItem . visible ) ;
chatUnreadMessagesCount . text = Functions . formatUnreadCount ( unreadCount )
2020-08-29 22:39:57 +03:00
}
2020-08-30 20:04:16 +03:00
onLastReadSentMessageUpdated: {
2020-11-20 23:58:26 +03:00
Debug . log ( "[ChatPage] Updating last read sent index, new index: " , lastReadSentIndex ) ;
2020-08-30 20:04:16 +03:00
chatView . lastReadSentIndex = lastReadSentIndex ;
}
2020-09-01 23:37:48 +03:00
onMessagesIncrementalUpdate: {
2023-12-28 15:03:13 +03:00
var proxyIndex = chatProxyModel . mapRowFromSource ( modelIndex , - 1 ) ;
Debug . log ( "Incremental update received. View now has " , chatView . count , " messages, view is on index " , proxyIndex , "(" + modelIndex + "), own messages were read before index " , lastReadSentIndex ) ;
2020-09-01 23:37:48 +03:00
chatView . lastReadSentIndex = lastReadSentIndex ;
2021-02-03 00:05:24 +03:00
if ( ! chatPage . isInitialized ) {
2023-12-28 15:03:13 +03:00
if ( proxyIndex > - 1 ) {
chatView . scrollToIndex ( proxyIndex ) ;
}
2021-02-03 00:05:24 +03:00
}
2021-02-03 01:19:02 +03:00
if ( chatView . height > chatView . contentHeight ) {
Debug . log ( "[ChatPage] Chat content quite small..." ) ;
viewMessageTimer . queueViewMessage ( chatView . count - 1 ) ;
2023-12-03 02:46:47 +03:00
} else if ( chatPage . messageIdToScrollTo && chatPage . messageIdToScrollTo != "" ) {
showMessage ( chatPage . messageIdToScrollTo , false )
2021-02-03 01:19:02 +03:00
}
2020-12-28 19:12:21 +03:00
chatViewCooldownTimer . restart ( ) ;
2021-02-16 23:48:14 +03:00
chatViewStartupReadTimer . restart ( ) ;
2020-09-01 23:37:48 +03:00
}
Reduce ChatPage.qml jit compile time
First of all: Take all measurements I mention with a grain of salt – all of them are rough and not necessarily measured more than a few times. All times were measured on an Xperia X run via SDK.
Visiting a chat page can take a long time, especially before the qml is cached by the engine.
When opening it for the first time after application launch, it sometimes takes >1000ms from onClicked (OverviewPage) to Component.OnCompleted (Chatpage).
Subsequent activations take roughly 470-480ms.
With these changes, I was able to reduce these times to ~450ms for the first, ~100ms for subsequent activations of the ChatPage on my test device.
Things changed:
- The components for displaying extra content to a message are (mostly) gone and replaced by a single Loader. This Loader does not use sourceComponent to trade the initial compilation boost for a neglegible bit of runtime penalty.
- Connections were consolidated
- I was surprised how costly the inclusion of the RemorseItem was (compiling ~75ms, initializing up to ~20ms for every delegate). So I traded a bit for a compromise. deleteMessageRemorseItem is now defined on the appWindow level, where it gets a bit mitigated by the animations at application start. Also, only one deletion at a time is now possible. We can easily revert this change, but I thought it worthwhile despite its drawbacks.
- profileThumbnailComponent is now defined directly as sourceComponent, removing the need for its id. Probably didn't do anything.
- InReplyToRow had width: parent.width, so I removed horizontalCenter. Also probably didn't change compilation time at all.
- Another compromise I was willing to take – your opinion may differ: The PickerPages took ages (~200ms) to just parse/compile inside those Components, so I replaced them with the "string notation" of pageStack.push. Drawback: The first time a picker gets activated, you'll see how slow it is. Subsequent activations aren't that bad – also for the other pickers.
2020-10-30 22:36:32 +03:00
onNotificationSettingsUpdated: {
chatInformation = chatModel . getChatInformation ( ) ;
muteChatMenuItem . text = chatInformation . notification_settings . mute_for > 0 ? qsTr ( "Unmute Chat" ) : qsTr ( "Mute Chat" ) ;
}
2020-11-17 14:18:46 +03:00
onPinnedMessageChanged: {
chatInformation = chatModel . getChatInformation ( ) ;
if ( chatInformation . pinned_message_id . toString ( ) !== "0" ) {
2020-11-20 23:58:26 +03:00
Debug . log ( "[ChatPage] Loading pinned message " , chatInformation . pinned_message_id ) ;
2020-11-17 14:18:46 +03:00
tdLibWrapper . getMessage ( chatInformation . id , chatInformation . pinned_message_id ) ;
} else {
pinnedMessageItem . pinnedMessage = undefined ;
}
}
2020-08-22 22:43:20 +03:00
}
2020-11-07 22:29:44 +03:00
Connections {
target: chatListModel
onChatJoined: {
2020-11-10 01:22:24 +03:00
appNotification . show ( qsTr ( "You joined the chat %1" ) . arg ( chatTitle ) ) ;
2020-11-07 22:29:44 +03:00
}
}
2020-10-18 19:57:01 +03:00
Timer {
id: lostFocusTimer
interval: 200
running: false
repeat: false
onTriggered: {
newMessageTextField . forceActiveFocus ( ) ;
}
}
Timer {
id: textReplacementTimer
interval: 600
running: false
repeat: false
onTriggered: {
handleMessageTextReplacement ( newMessageTextField . text , newMessageTextField . cursorPosition ) ;
}
}
2020-08-21 19:03:51 +03:00
Timer {
id: chatContactTimeUpdater
interval: 60000
2020-11-26 02:25:15 +03:00
running: isPrivateChat || isSecretChat
2020-08-21 19:03:51 +03:00
repeat: true
onTriggered: {
updateChatPartnerStatusText ( ) ;
}
}
2020-11-04 14:26:57 +03:00
Timer {
id: viewMessageTimer
2021-02-04 00:16:22 +03:00
interval: appSettings . delayMessageRead ? 1000 : 0
2020-11-04 14:26:57 +03:00
property int lastQueuedIndex: - 1
function queueViewMessage ( index ) {
2020-11-05 02:02:27 +03:00
if ( index > lastQueuedIndex ) {
2020-11-04 14:26:57 +03:00
lastQueuedIndex = index ;
2020-11-05 02:02:27 +03:00
start ( ) ;
2020-11-04 14:26:57 +03:00
}
}
onTriggered: {
2021-12-15 01:22:21 +03:00
Debug . log ( "scroll position changed, message index: " , lastQueuedIndex ) ;
Debug . log ( "unread count: " , chatInformation . unread_count ) ;
2023-12-28 15:03:13 +03:00
var modelIndex = chatProxyModel . mapRowToSource ( lastQueuedIndex ) ;
var messageToRead = chatModel . getMessage ( modelIndex ) ;
2021-12-06 00:06:05 +03:00
if ( messageToRead [ '@type' ] === "sponsoredMessage" ) {
2021-12-15 01:22:21 +03:00
Debug . log ( "sponsored message to read: " , messageToRead . id ) ;
2022-01-08 00:03:58 +03:00
tdLibWrapper . viewMessage ( chatInformation . id , messageToRead . message_id , false ) ;
2021-12-06 00:06:05 +03:00
} else if ( chatInformation . unread_count > 0 && lastQueuedIndex > - 1 ) {
2023-12-28 15:03:13 +03:00
if ( messageToRead ) {
Debug . log ( "message to read: " , messageToRead . id ) ;
var messageId = messageToRead . id ;
var type = messageToRead . content [ "@type" ] ;
if ( messageToRead . media_album_id !== '0' ) {
var albumIds = chatModel . getMessageIdsForAlbum ( messageToRead . media_album_id ) ;
if ( albumIds . length > 0 ) {
messageId = albumIds [ albumIds . length - 1 ] ;
Debug . log ( "message to read last album message id: " , messageId ) ;
}
}
if ( messageId ) {
tdLibWrapper . viewMessage ( chatInformation . id , messageId , false ) ;
}
2020-11-04 14:26:57 +03:00
}
lastQueuedIndex = - 1
}
2020-12-28 23:57:34 +03:00
if ( chatInformation . unread_count === 0 ) {
tdLibWrapper . readAllChatMentions ( chatInformation . id ) ;
2022-05-24 22:19:15 +03:00
tdLibWrapper . readAllChatReactions ( chatInformation . id ) ;
2020-12-28 23:57:34 +03:00
}
2020-11-04 14:26:57 +03:00
}
}
2020-08-21 19:03:51 +03:00
2021-02-14 23:57:48 +03:00
Drawer {
id: messageOptionsDrawer
property var myMessage: ( { } )
property var userInformation: ( { } )
2021-02-15 23:20:38 +03:00
property var additionalItemsModel: 0
property var sourceItem
2021-12-05 04:25:24 +03:00
property bool showCopyMessageToClipboardMenuItem
property bool showForwardMessageMenuItem
property bool showDeleteMessageMenuItem
2021-02-14 23:57:48 +03:00
2021-02-16 22:54:48 +03:00
property list < NamedAction > messageOptionsModel: [
NamedAction {
2021-12-05 04:25:24 +03:00
visible: messageOptionsDrawer . showCopyMessageToClipboardMenuItem
2021-02-16 22:54:48 +03:00
name: qsTr ( "Copy Message to Clipboard" )
2023-12-04 19:12:18 +03:00
action: messageOptionsDrawer . sourceItem . copyMessageToClipboard
2021-02-16 22:54:48 +03:00
} ,
2021-02-18 01:48:08 +03:00
NamedAction {
2021-12-05 04:25:24 +03:00
visible: messageOptionsDrawer . showForwardMessageMenuItem && messageOptionsDrawer . myMessage . can_be_forwarded
2021-02-18 01:48:08 +03:00
name: qsTr ( "Forward Message" )
action: function ( ) {
2021-12-05 04:25:24 +03:00
startForwardingMessages ( [ messageOptionsDrawer . myMessage ] )
2021-02-18 01:48:08 +03:00
}
} ,
2021-02-16 22:54:48 +03:00
NamedAction {
visible: canPinMessages ( )
name: messageOptionsDrawer . myMessage . is_pinned ? qsTr ( "Unpin Message" ) : qsTr ( "Pin Message" )
action: function ( ) {
if ( messageOptionsDrawer . myMessage . is_pinned ) {
Remorse . popupAction ( page , qsTr ( "Message unpinned" ) , function ( ) { tdLibWrapper . unpinMessage ( chatPage . chatInformation . id , messageOptionsDrawer . myMessage . id ) ;
pinnedMessageItem . requestCloseMessage ( ) ; } ) ;
} else {
tdLibWrapper . pinMessage ( chatPage . chatInformation . id , messageOptionsDrawer . myMessage . id ) ;
}
}
} ,
NamedAction {
2021-12-05 04:25:24 +03:00
visible: messageOptionsDrawer . showDeleteMessageMenuItem
2021-02-16 22:54:48 +03:00
name: qsTr ( "Delete Message" )
2021-12-05 04:25:24 +03:00
action: messageOptionsDrawer . sourceItem . deleteMessage
2021-02-16 22:54:48 +03:00
}
]
onOpenChanged: {
if ( open ) {
var jointModel = [ ] ;
for ( var j = 0 ; j < additionalItemsModel . length ; j ++ ) {
jointModel . push ( additionalItemsModel [ j ] ) ;
}
for ( var i = 0 ; i < messageOptionsModel . length ; i ++ ) {
2021-12-05 04:25:24 +03:00
var item = messageOptionsModel [ i ]
if ( item . visible ) jointModel . push ( item )
2021-02-16 22:54:48 +03:00
}
drawerListView . model = jointModel ;
2021-02-21 02:26:39 +03:00
focus = true // Take the focus away from the text field
2021-02-16 22:54:48 +03:00
}
}
2020-08-21 15:26:56 +03:00
anchors.fill: parent
2021-02-15 23:20:38 +03:00
dock: chatPage . isPortrait ? Dock.Bottom : Dock . Right
2021-02-16 20:51:50 +03:00
backgroundSize: chatPage . isPortrait ? height / 3 : width / 2
2021-02-14 23:57:48 +03:00
2021-02-16 22:54:48 +03:00
background: SilicaListView {
id: drawerListView
2021-02-14 23:57:48 +03:00
anchors.fill: parent
2021-02-16 22:54:48 +03:00
clip: true
VerticalScrollDecorator { }
header: Row {
2021-02-15 23:20:38 +03:00
id: drawerHeaderRow
width: parent . width - ( 2 * Theme . horizontalPageMargin )
2021-02-14 23:57:48 +03:00
height: messageOptionsLabel . height + Theme . paddingLarge + ( chatPage . isPortrait ? ( 2 * Theme . paddingSmall ) : 0 )
anchors.horizontalCenter: parent . horizontalCenter
spacing: Theme . paddingMedium
Label {
id: messageOptionsLabel
2021-02-15 23:20:38 +03:00
text: qsTr ( "Additional Options" )
2021-02-14 23:57:48 +03:00
color: Theme . highlightColor
font.pixelSize: Theme . fontSizeLarge
width: parent . width - closeMessageOptionsButton . width - Theme . paddingMedium
anchors.verticalCenter: parent . verticalCenter
horizontalAlignment: Text . AlignRight
}
IconButton {
id: closeMessageOptionsButton
icon.source: "image://theme/icon-m-clear"
anchors.verticalCenter: parent . verticalCenter
onClicked: {
2021-02-21 02:26:39 +03:00
messageOptionsDrawer . open = false
2020-09-16 21:43:36 +03:00
}
}
}
2020-12-27 02:16:25 +03:00
2021-02-16 22:54:48 +03:00
delegate: ListItem {
Label {
width: parent . width - ( 2 * Theme . horizontalPageMargin )
text: modelData . name
anchors.verticalCenter: parent . verticalCenter
anchors.horizontalCenter: parent . horizontalCenter
horizontalAlignment: Text . AlignHCenter
}
onClicked: {
modelData . action ( ) ;
2021-02-21 02:26:39 +03:00
messageOptionsDrawer . open = false
2020-12-27 02:16:25 +03:00
}
2021-02-16 22:54:48 +03:00
hidden: ! modelData . visible
2020-12-27 02:16:25 +03:00
}
2020-09-16 21:43:36 +03:00
}
2021-02-14 23:57:48 +03:00
SilicaFlickable {
id: chatContainer
onContentYChanged: {
// For some strange reason contentY sometimes is > 0 which doesn't make sense without a PushUpMenu (?)
// That leads to the problem that the whole flickable is moved slightly (or sometimes considerably) up
// which creates UX issues... As a workaround we are setting it to 0 in such cases.
// Better solutions are highly appreciated, contributions always welcome! ;)
if ( contentY > 0 ) {
contentY = 0 ;
2020-11-15 01:50:12 +03:00
}
2020-10-19 13:20:02 +03:00
}
2020-11-16 23:39:11 +03:00
2021-02-14 23:57:48 +03:00
anchors.fill: parent
contentHeight: height
contentWidth: width
2020-08-21 15:26:56 +03:00
2021-02-14 23:57:48 +03:00
PullDownMenu {
visible: chatInformation . id !== chatPage . myUserId && ! stickerPickerLoader . active && ! voiceNoteOverlayLoader . active && ! messageOverlayLoader . active && ! stickerSetOverlayLoader . active
2021-12-09 00:35:26 +03:00
MenuItem {
id: deleteChatMenuItem
visible: chatPage . isPrivateChat
onClicked: {
var privateChatId = chatInformation . id ;
Remorse . popupAction ( chatPage , qsTr ( "Deleting chat" ) , function ( ) {
tdLibWrapper . deleteChat ( privateChatId ) ;
pageStack . pop ( pageStack . find ( function ( page ) { return ( page . _depth === 0 ) } ) ) ;
} , 10000 ) ;
}
text: qsTr ( "Delete Chat" )
}
2021-02-14 23:57:48 +03:00
MenuItem {
id: closeSecretChatMenuItem
visible: chatPage . isSecretChat && chatPage . secretChatDetails . state [ "@type" ] !== "secretChatStateClosed"
onClicked: {
var secretChatId = chatPage . secretChatDetails . id ;
Remorse . popupAction ( chatPage , qsTr ( "Closing chat" ) , function ( ) { tdLibWrapper . closeSecretChat ( secretChatId ) } ) ;
}
text: qsTr ( "Close Chat" )
}
2020-08-21 15:26:56 +03:00
2021-02-14 23:57:48 +03:00
MenuItem {
id: joinLeaveChatMenuItem
visible: ( chatPage . isSuperGroup || chatPage . isBasicGroup ) && chatGroupInformation && chatGroupInformation . status [ "@type" ] !== "chatMemberStatusBanned"
onClicked: {
if ( chatPage . userIsMember ) {
var chatId = chatInformation . id ;
Remorse . popupAction ( chatPage , qsTr ( "Leaving chat" ) , function ( ) {
tdLibWrapper . leaveChat ( chatId ) ;
// this does not care about the response (ideally type "ok" without further reference) for now
pageStack . pop ( pageStack . find ( function ( page ) { return ( page . _depth === 0 ) } ) ) ;
} ) ;
} else {
tdLibWrapper . joinChat ( chatInformation . id ) ;
2020-11-19 22:25:53 +03:00
}
}
2021-02-14 23:57:48 +03:00
text: chatPage . userIsMember ? qsTr ( "Leave Chat" ) : qsTr ( "Join Chat" )
}
2020-11-25 02:23:38 +03:00
2021-02-14 23:57:48 +03:00
MenuItem {
id: muteChatMenuItem
visible: chatPage . userIsMember
onClicked: {
var newNotificationSettings = chatInformation . notification_settings ;
if ( newNotificationSettings . mute_for > 0 ) {
newNotificationSettings . mute_for = 0 ;
} else {
newNotificationSettings . mute_for = 6666666 ;
}
newNotificationSettings . use_default_mute_for = false ;
tdLibWrapper . setChatNotificationSettings ( chatInformation . id , newNotificationSettings ) ;
2020-11-25 02:23:38 +03:00
}
2021-02-14 23:57:48 +03:00
text: chatInformation . notification_settings . mute_for > 0 ? qsTr ( "Unmute Chat" ) : qsTr ( "Mute Chat" )
}
2020-11-25 02:23:38 +03:00
2021-02-14 23:57:48 +03:00
MenuItem {
id: searchInChatMenuItem
visible: ! chatPage . isSecretChat && chatOverviewItem . visible
onClicked: {
// This automatically shows the search field as well
chatOverviewItem . visible = false ;
searchInChatField . focus = true ;
2020-11-25 02:23:38 +03:00
}
2021-02-14 23:57:48 +03:00
text: qsTr ( "Search in Chat" )
2020-08-21 15:26:56 +03:00
}
2021-02-14 23:57:48 +03:00
}
2020-08-21 15:26:56 +03:00
2021-02-14 23:57:48 +03:00
BackgroundItem {
id: headerMouseArea
height: headerRow . height
width: parent . width
onClicked: {
if ( chatPage . isSelecting ) {
chatPage . selectedMessages = [ ] ;
} else {
pageStack . navigateForward ( ) ;
2020-08-21 15:26:56 +03:00
}
}
2021-02-14 23:57:48 +03:00
}
2020-12-27 02:16:25 +03:00
2021-02-14 23:57:48 +03:00
Column {
id: chatColumn
width: parent . width
height: parent . height
2020-12-27 02:16:25 +03:00
2021-02-14 23:57:48 +03:00
Row {
id: headerRow
width: parent . width - ( 3 * Theme . horizontalPageMargin )
height: chatOverviewItem . height + ( chatPage . isPortrait ? ( 2 * Theme . paddingMedium ) : ( 2 * Theme . paddingSmall ) )
anchors.horizontalCenter: parent . horizontalCenter
spacing: Theme . paddingMedium
2020-12-27 02:16:25 +03:00
2021-02-14 23:57:48 +03:00
Item {
width: chatOverviewItem . height
height: chatOverviewItem . height
anchors.bottom: parent . bottom
anchors.bottomMargin: chatPage . isPortrait ? Theme.paddingMedium : Theme . paddingSmall
ProfileThumbnail {
id: chatPictureThumbnail
replacementStringHint: chatNameText . text
width: parent . height
height: parent . height
// Setting it directly may cause an stale state for the thumbnail in case the chat page
// was previously loaded with a picture and now it doesn't have one. Instead setting it
// when the ChatModel indicates a change. This also avoids flickering when the page is loaded...
Connections {
target: chatModel
onSmallPhotoChanged: {
chatPictureThumbnail . photoData = chatModel . smallPhoto ;
}
}
2020-12-27 02:16:25 +03:00
}
2021-02-14 23:57:48 +03:00
Rectangle {
id: chatSecretBackground
color: Theme . rgba ( Theme . overlayBackgroundColor , Theme . opacityFaint )
width: chatPage . isPortrait ? Theme.fontSizeLarge : Theme . fontSizeMedium
height: width
anchors.left: parent . left
anchors.bottom: parent . bottom
radius: parent . width / 2
visible: chatPage . isSecretChat
2020-12-27 02:16:25 +03:00
}
2020-08-21 15:26:56 +03:00
2021-02-14 23:57:48 +03:00
Image {
source: "image://theme/icon-s-secure"
width: chatPage . isPortrait ? Theme.fontSizeSmall : Theme . fontSizeExtraSmall
height: width
anchors.centerIn: chatSecretBackground
visible: chatPage . isSecretChat
}
2020-11-16 23:39:11 +03:00
2021-02-14 23:57:48 +03:00
}
2020-08-21 15:26:56 +03:00
2021-02-14 23:57:48 +03:00
Item {
id: chatOverviewItem
opacity: visible ? 1 : 0
Behavior on opacity { FadeAnimation { } }
width: parent . width - chatPictureThumbnail . width - Theme . paddingMedium
height: chatNameText . height + chatStatusText . height
anchors.bottom: parent . bottom
anchors.bottomMargin: chatPage . isPortrait ? Theme.paddingMedium : Theme . paddingSmall
Label {
id: chatNameText
width: Math . min ( implicitWidth , parent . width )
anchors.right: parent . right
text: chatInformation . title !== "" ? Emoji . emojify ( chatInformation . title , font . pixelSize ) : qsTr ( "Unknown" )
textFormat: Text . StyledText
font.pixelSize: chatPage . isPortrait ? Theme.fontSizeLarge : Theme . fontSizeMedium
font.family: Theme . fontFamilyHeading
color: Theme . highlightColor
truncationMode: TruncationMode . Fade
maximumLineCount: 1
}
Label {
id: chatStatusText
width: Math . min ( implicitWidth , parent . width )
anchors {
right: parent . right
bottom: parent . bottom
}
text: ""
textFormat: Text . StyledText
font.pixelSize: chatPage . isPortrait ? Theme.fontSizeExtraSmall : Theme . fontSizeTiny
font.family: Theme . fontFamilyHeading
color: headerMouseArea . pressed ? Theme.secondaryHighlightColor : Theme . secondaryColor
truncationMode: TruncationMode . Fade
maximumLineCount: 1
}
}
2020-09-18 23:55:59 +03:00
2021-02-14 23:57:48 +03:00
Item {
id: searchInChatItem
visible: ! chatOverviewItem . visible
opacity: visible ? 1 : 0
Behavior on opacity { FadeAnimation { } }
width: parent . width - chatPictureThumbnail . width - Theme . paddingMedium
height: searchInChatField . height
anchors.bottom: parent . bottom
anchors.bottomMargin: chatPage . isPortrait ? Theme.paddingSmall : 0
SearchField {
id: searchInChatField
visible: false
width: visible ? parent.width : 0
placeholderText: qsTr ( "Search in chat..." )
active: searchInChatItem . visible
canHide: text === ""
onTextChanged: {
searchInChatTimer . restart ( ) ;
}
2020-09-18 23:55:59 +03:00
2021-02-14 23:57:48 +03:00
onHideClicked: {
resetFocus ( ) ;
}
2020-09-18 23:55:59 +03:00
2021-02-14 23:57:48 +03:00
EnterKey.iconSource: "image://theme/icon-m-enter-close"
EnterKey.onClicked: {
resetFocus ( ) ;
}
2021-02-03 00:05:24 +03:00
}
2020-09-22 22:05:24 +03:00
}
}
2021-02-16 23:55:04 +03:00
2021-02-14 23:57:48 +03:00
PinnedMessageItem {
id: pinnedMessageItem
onRequestShowMessage: {
messageOverlayLoader . overlayMessage = pinnedMessageItem . pinnedMessage ;
messageOverlayLoader . active = true ;
}
onRequestCloseMessage: {
messageOverlayLoader . overlayMessage = undefined ;
messageOverlayLoader . active = false ;
2020-11-18 16:22:08 +03:00
}
}
Reduce ChatPage.qml jit compile time
First of all: Take all measurements I mention with a grain of salt – all of them are rough and not necessarily measured more than a few times. All times were measured on an Xperia X run via SDK.
Visiting a chat page can take a long time, especially before the qml is cached by the engine.
When opening it for the first time after application launch, it sometimes takes >1000ms from onClicked (OverviewPage) to Component.OnCompleted (Chatpage).
Subsequent activations take roughly 470-480ms.
With these changes, I was able to reduce these times to ~450ms for the first, ~100ms for subsequent activations of the ChatPage on my test device.
Things changed:
- The components for displaying extra content to a message are (mostly) gone and replaced by a single Loader. This Loader does not use sourceComponent to trade the initial compilation boost for a neglegible bit of runtime penalty.
- Connections were consolidated
- I was surprised how costly the inclusion of the RemorseItem was (compiling ~75ms, initializing up to ~20ms for every delegate). So I traded a bit for a compromise. deleteMessageRemorseItem is now defined on the appWindow level, where it gets a bit mitigated by the animations at application start. Also, only one deletion at a time is now possible. We can easily revert this change, but I thought it worthwhile despite its drawbacks.
- profileThumbnailComponent is now defined directly as sourceComponent, removing the need for its id. Probably didn't do anything.
- InReplyToRow had width: parent.width, so I removed horizontalCenter. Also probably didn't change compilation time at all.
- Another compromise I was willing to take – your opinion may differ: The PickerPages took ages (~200ms) to just parse/compile inside those Components, so I replaced them with the "string notation" of pageStack.push. Drawback: The first time a picker gets activated, you'll see how slow it is. Subsequent activations aren't that bad – also for the other pickers.
2020-10-30 22:36:32 +03:00
2021-02-14 23:57:48 +03:00
Item {
id: chatViewItem
width: parent . width
height: parent . height - headerRow . height - pinnedMessageItem . height - newMessageColumn . height - selectedMessagesActions . height
property int previousHeight ;
2020-11-15 01:50:12 +03:00
2021-02-14 23:57:48 +03:00
Component.onCompleted: {
previousHeight = height ;
2020-11-07 22:58:23 +03:00
}
2020-08-30 20:04:16 +03:00
2021-02-14 23:57:48 +03:00
onHeightChanged: {
2021-05-26 00:15:43 +03:00
if ( previousHeight > height ) {
var deltaHeight = previousHeight - height ;
chatView . contentY = chatView . contentY + deltaHeight ;
} else {
chatView . handleScrollPositionChanged ( ) ;
}
2021-02-14 23:57:48 +03:00
previousHeight = height ;
}
Timer {
id: chatViewCooldownTimer
interval: 2000
repeat: false
running: false
onTriggered: {
Debug . log ( "[ChatPage] Cooldown completed..." ) ;
chatView . inCooldown = false ;
if ( ! chatPage . isInitialized ) {
Debug . log ( "Page is initialized!" ) ;
chatPage . isInitialized = true ;
chatView . handleScrollPositionChanged ( ) ;
2020-11-02 22:54:18 +03:00
}
}
}
2021-02-16 23:55:04 +03:00
Timer {
id: chatViewStartupReadTimer
interval: 200
repeat: false
running: false
onTriggered: {
if ( ! chatPage . isInitialized ) {
Debug . log ( "Page is initialized!" ) ;
chatPage . isInitialized = true ;
chatView . handleScrollPositionChanged ( ) ;
2021-12-06 00:06:05 +03:00
if ( chatPage . isChannel ) {
2022-01-08 00:03:58 +03:00
tdLibWrapper . getChatSponsoredMessage ( chatInformation . id ) ;
2021-12-06 00:06:05 +03:00
}
2021-12-11 20:29:31 +03:00
if ( typeof chatPage . messageToShow !== "undefined" && chatPage . messageToShow !== { } ) {
messageOverlayLoader . overlayMessage = chatPage . messageToShow ;
messageOverlayLoader . active = true ;
}
2021-12-26 05:50:37 +03:00
if ( chatPage . messageIdToShow ) {
2021-12-11 20:29:31 +03:00
tdLibWrapper . getMessage ( chatPage . chatInformation . id , chatPage . messageIdToShow ) ;
}
2020-11-02 22:54:18 +03:00
}
}
2020-08-29 19:04:23 +03:00
}
2021-02-14 23:57:48 +03:00
Loader {
asynchronous: true
active: chatView . blurred
anchors.fill: chatView
sourceComponent: Component {
FastBlur {
source: chatView
radius: Theme . paddingLarge
2020-11-02 22:54:18 +03:00
}
}
2020-08-29 19:04:23 +03:00
}
2021-02-14 23:57:48 +03:00
SilicaListView {
id: chatView
visible: ! blurred
property bool blurred: messageOverlayLoader . item || stickerPickerLoader . item || voiceNoteOverlayLoader . item || inlineQuery . hasOverlay || stickerSetOverlayLoader . item
anchors.fill: parent
opacity: chatPage . loading ? 0 : 1
Behavior on opacity { FadeAnimation { } }
clip: true
highlightMoveDuration: 0
highlightResizeDuration: 0
property int lastReadSentIndex: - 1
property bool inCooldown: false
property bool manuallyScrolledToBottom
property QtObject precalculatedValues: QtObject {
readonly property alias page: chatPage
readonly property bool showUserInfo: page . isBasicGroup || ( page . isSuperGroup && ! page . isChannel )
readonly property int profileThumbnailDimensions: showUserInfo ? Theme.itemSizeSmall : 0
readonly property int pageMarginDouble: 2 * Theme . horizontalPageMargin
readonly property int paddingMediumDouble: 2 * Theme . paddingMedium
2023-12-03 14:59:41 +03:00
readonly property int entryWidth: chatView . width - pageMarginDouble
2021-02-14 23:57:48 +03:00
readonly property int textItemWidth: entryWidth - profileThumbnailDimensions - Theme . paddingSmall
2023-07-12 01:27:18 +03:00
readonly property int backgroundWidth: page . isPrivateChat ? textItemWidth - pageMarginDouble : textItemWidth //уменьшенная ширина сообщений для приватных чатов
2021-02-14 23:57:48 +03:00
readonly property int backgroundRadius: textItemWidth / 50
readonly property int textColumnWidth: backgroundWidth - Theme . horizontalPageMargin
readonly property int messageInReplyToHeight: Theme . fontSizeExtraSmall * 2.571428571 + Theme . paddingSmall ;
readonly property int webPagePreviewHeight: ( ( textColumnWidth * 2 / 3 ) + ( 6 * Theme . fontSizeExtraSmall ) + ( 7 * Theme . paddingSmall ) )
readonly property bool pageIsSelecting: chatPage . isSelecting
}
function handleScrollPositionChanged ( ) {
Debug . log ( "Current position: " , chatView . contentY ) ;
2021-12-06 00:06:05 +03:00
Debug . log ( "Contains sponsored messages?" , chatPage . containsSponsoredMessages ) ;
if ( chatOverviewItem . visible && ( chatInformation . unread_count > 0 || chatPage . containsSponsoredMessages ) ) {
2021-02-14 23:57:48 +03:00
var bottomIndex = chatView . indexAt ( chatView . contentX , ( chatView . contentY + chatView . height - Theme . horizontalPageMargin ) ) ;
if ( bottomIndex > - 1 ) {
viewMessageTimer . queueViewMessage ( bottomIndex )
}
} else {
tdLibWrapper . readAllChatMentions ( chatInformation . id ) ;
2022-06-06 16:55:21 +03:00
tdLibWrapper . readAllChatReactions ( chatInformation . id ) ;
2020-11-16 01:05:22 +03:00
}
2021-02-14 23:57:48 +03:00
manuallyScrolledToBottom = chatView . atYEnd
2020-08-30 16:04:15 +03:00
}
2020-08-29 22:39:57 +03:00
2023-12-03 14:53:32 +03:00
function scrollToIndex ( index , mode ) {
2021-02-14 23:57:48 +03:00
if ( index > 0 && index < chatView . count ) {
2023-12-03 14:53:32 +03:00
positionViewAtIndex ( index , ( mode === undefined ) ? ListView.Contain : mode )
2021-02-14 23:57:48 +03:00
if ( index === chatView . count - 1 ) {
manuallyScrolledToBottom = true ;
2023-12-28 15:03:13 +03:00
if ( ! chatView . atYEnd ) {
chatView . positionViewAtEnd ( ) ;
}
2021-02-14 23:57:48 +03:00
}
}
}
2020-08-23 00:05:45 +03:00
2021-02-14 23:57:48 +03:00
onContentYChanged: {
if ( ! chatPage . loading && ! chatView . inCooldown ) {
if ( chatView . indexAt ( chatView . contentX , chatView . contentY ) < 10 ) {
Debug . log ( "[ChatPage] Trying to get older history items..." ) ;
chatView . inCooldown = true ;
chatModel . triggerLoadMoreHistory ( ) ;
} else if ( chatOverviewItem . visible && chatView . indexAt ( chatView . contentX , chatView . contentY ) > ( count - 10 ) ) {
Debug . log ( "[ChatPage] Trying to get newer history items..." ) ;
chatView . inCooldown = true ;
chatModel . triggerLoadMoreFuture ( ) ;
}
}
}
onMovementEnded: {
2020-08-29 19:04:23 +03:00
handleScrollPositionChanged ( ) ;
2021-02-14 23:57:48 +03:00
}
onQuickScrollAnimatingChanged: {
if ( ! quickScrollAnimating ) {
handleScrollPositionChanged ( ) ;
if ( atYEnd ) { // handle some false guesses from quick scroll
chatView . scrollToIndex ( chatView . count - 2 )
chatView . scrollToIndex ( chatView . count - 1 )
}
2020-11-02 22:54:18 +03:00
}
2020-08-29 19:04:23 +03:00
}
2023-12-28 15:03:13 +03:00
BoolFilterModel {
id: chatProxyModel
sourceModel: chatModel
filterRoleName: "album_entry_filter"
filterValue: false
}
model: chatProxyModel
2021-02-14 23:57:48 +03:00
header: Component {
Loader {
active: ! ! chatPage . botInformation
&& ! ! chatPage . botInformation . bot_info && chatPage . botInformation . bot_info . description . length > 0
asynchronous: true
width: chatView . width
sourceComponent: Component {
Label {
id: botInfoLabel
topPadding: Theme . paddingLarge
bottomPadding: Theme . paddingLarge
leftPadding: Theme . horizontalPageMargin
rightPadding: Theme . horizontalPageMargin
text: Emoji . emojify ( chatPage . botInformation . bot_info . description , font . pixelSize )
font.pixelSize: Theme . fontSizeSmall
color: Theme . highlightColor
wrapMode: Text . Wrap
textFormat: Text . StyledText
horizontalAlignment: Text . AlignHCenter
onLinkActivated: {
var chatCommand = Functions . handleLink ( link ) ;
if ( chatCommand ) {
tdLibWrapper . sendTextMessage ( chatInformation . id , chatCommand ) ;
}
2021-01-10 04:06:41 +03:00
}
2021-02-14 23:57:48 +03:00
linkColor: Theme . primaryColor
visible: ( text !== "" )
2021-01-10 04:06:41 +03:00
}
}
}
}
2023-12-28 15:03:13 +03:00
function getContentComponentHeight ( contentType , content , parentWidth , albumEntries ) {
var unit ;
2021-02-14 23:57:48 +03:00
switch ( contentType ) {
2021-12-05 00:05:22 +03:00
case "messageAnimatedEmoji" :
return content . animated_emoji . sticker . height ;
2021-02-14 23:57:48 +03:00
case "messageAnimation" :
return Functions . getVideoHeight ( parentWidth , content . animation ) ;
case "messageAudio" :
case "messageVoiceNote" :
case "messageDocument" :
return Theme . itemSizeLarge ;
case "messageGame" :
return parentWidth * 0.66666666 + Theme . itemSizeLarge ; // 2 / 3;
case "messageLocation" :
case "messageVenue" :
return parentWidth * 0.66666666 ; // 2 / 3;
case "messagePhoto" :
2023-12-28 15:03:13 +03:00
if ( albumEntries > 0 ) {
unit = ( parentWidth * 0.66666666 )
return ( albumEntries % 2 !== 0 ? unit * 0.75 : 0 ) + unit * albumEntries * 0.25
}
2021-02-14 23:57:48 +03:00
var biggest = content . photo . sizes [ content . photo . sizes . length - 1 ] ;
var aspectRatio = biggest . width / biggest . height ;
return Math . max ( Theme . itemSizeExtraSmall , Math . min ( parentWidth * 0.66666666 , parentWidth / aspectRatio ) ) ;
case "messagePoll" :
return Theme . itemSizeSmall * ( 4 + content . poll . options ) ;
case "messageSticker" :
return content . sticker . height ;
case "messageVideo" :
2023-12-28 15:03:13 +03:00
if ( albumEntries > 0 ) {
unit = ( parentWidth * 0.66666666 )
return ( albumEntries % 2 !== 0 ? unit * 0.75 : 0 ) + unit * albumEntries * 0.25
}
2021-02-14 23:57:48 +03:00
return Functions . getVideoHeight ( parentWidth , content . video ) ;
case "messageVideoNote" :
return parentWidth
}
2020-10-31 22:02:18 +03:00
}
2020-08-21 15:26:56 +03:00
2021-02-14 23:57:48 +03:00
readonly property var delegateMessagesContent: [
2021-12-05 00:05:22 +03:00
"messageAnimatedEmoji" ,
2021-02-14 23:57:48 +03:00
"messageAnimation" ,
"messageAudio" ,
// "messageContact",
// "messageDice"
"messageDocument" ,
"messageGame" ,
// "messageInvoice",
"messageLocation" ,
// "messagePassportDataSent",
// "messagePaymentSuccessful",
"messagePhoto" ,
"messagePoll" ,
// "messageProximityAlertTriggered",
"messageSticker" ,
"messageVenue" ,
"messageVideo" ,
"messageVideoNote" ,
"messageVoiceNote"
]
readonly property var simpleDelegateMessages: [ "messageBasicGroupChatCreate" ,
"messageChatAddMembers" ,
"messageChatChangePhoto" ,
"messageChatChangeTitle" ,
"messageChatDeleteMember" ,
"messageChatDeletePhoto" ,
"messageChatJoinByLink" ,
"messageChatSetTtl" ,
"messageChatUpgradeFrom" ,
2021-02-25 23:38:55 +03:00
"messageContactRegistered" ,
// "messageExpiredPhoto", "messageExpiredVideo","messageWebsiteConnected"
2021-02-14 23:57:48 +03:00
"messageGameScore" ,
"messageChatUpgradeTo" ,
"messageCustomServiceAction" ,
"messagePinMessage" ,
"messageScreenshotTaken" ,
"messageSupergroupChatCreate" ,
"messageUnsupported" ]
delegate: Loader {
width: chatView . width
Component {
id: messageListViewItemComponent
MessageListViewItem {
precalculatedValues: chatView . precalculatedValues
chatId: chatModel . chatId
myMessage: model . display
messageId: model . message_id
2023-12-28 15:03:13 +03:00
messageAlbumMessageIds: model . album_message_ids
2021-02-14 23:57:48 +03:00
messageViewCount: model . view_count
2022-05-24 00:33:17 +03:00
reactions: model . reactions
2023-11-18 16:45:22 +03:00
chatReactions: availableReactions
2023-12-28 15:03:13 +03:00
messageIndex: chatProxyModel . mapRowToSource ( model . index )
2021-02-14 23:57:48 +03:00
hasContentComponent: ! ! myMessage . content && chatView . delegateMessagesContent . indexOf ( model . content_type ) > - 1
canReplyToMessage: chatPage . canSendMessages
onReplyToMessage: {
newMessageInReplyToRow . inReplyToMessage = myMessage
newMessageTextField . focus = true
}
onEditMessage: {
newMessageColumn . editMessageId = messageId
newMessageTextField . text = Functions . getMessageText ( myMessage , false , chatPage . myUserId , true )
newMessageTextField . focus = true
}
2021-12-05 04:25:24 +03:00
onForwardMessage: {
startForwardingMessages ( [ myMessage ] )
}
2020-12-15 21:15:12 +03:00
}
2020-11-07 22:58:23 +03:00
}
2021-02-14 23:57:48 +03:00
Component {
id: messageListViewItemSimpleComponent
MessageListViewItemSimple { }
}
2023-12-28 15:03:13 +03:00
Component {
id: messageListViewItemHiddenComponent
Item {
property var myMessage: display
property bool senderIsUser: myMessage . sender_id [ "@type" ] === "messageSenderUser"
property var userInformation: senderIsUser ? tdLibWrapper . getUserInformation ( myMessage . sender_id . user_id ) : null
property bool isOwnMessage: senderIsUser && chatPage . myUserId === myMessage . sender_id . user_id
height: 1
}
}
sourceComponent: chatView . simpleDelegateMessages . indexOf ( model . content_type ) > - 1
? messageListViewItemSimpleComponent
: messageListViewItemComponent
2020-09-15 23:47:39 +03:00
}
2023-12-28 15:03:13 +03:00
VerticalScrollDecorator { flickable: chatView }
2020-10-19 13:20:02 +03:00
2021-02-14 23:57:48 +03:00
ViewPlaceholder {
id: chatViewPlaceholder
enabled: chatView . count === 0
text: ( chatPage . isSecretChat && ! chatPage . isSecretChatReady ) ? qsTr ( "This secret chat is not yet ready. Your chat partner needs to go online first." ) : qsTr ( "This chat is empty." )
}
2020-10-19 13:20:02 +03:00
}
2020-08-29 19:04:23 +03:00
2021-02-14 23:57:48 +03:00
Column {
width: parent . width
height: loadingLabel . height + loadingBusyIndicator . height + Theme . paddingMedium
spacing: Theme . paddingMedium
anchors.verticalCenter: parent . verticalCenter
2020-08-29 19:04:23 +03:00
2021-02-14 23:57:48 +03:00
opacity: chatPage . loading ? 1 : 0
Behavior on opacity { FadeAnimation { } }
visible: chatPage . loading
2020-08-29 19:04:23 +03:00
2021-02-14 23:57:48 +03:00
InfoLabel {
id: loadingLabel
text: qsTr ( "Loading messages..." )
}
2020-08-29 19:04:23 +03:00
2021-02-14 23:57:48 +03:00
BusyIndicator {
id: loadingBusyIndicator
anchors.horizontalCenter: parent . horizontalCenter
running: chatPage . loading
size: BusyIndicatorSize . Large
}
2020-08-29 19:04:23 +03:00
}
2020-08-21 15:26:56 +03:00
2021-02-14 23:57:48 +03:00
Item {
id: chatUnreadMessagesItem
2023-08-03 15:02:34 +03:00
width: chatUnreadMessagesCount . width + Theme . fontSizeLarge / 2
2021-02-14 23:57:48 +03:00
height: Theme . fontSizeHuge
anchors.right: parent . right
anchors.rightMargin: Theme . paddingMedium
anchors.bottom: parent . bottom
anchors.bottomMargin: Theme . paddingMedium
visible: ! chatPage . loading && chatInformation . unread_count > 0 && chatOverviewItem . visible
Rectangle {
id: chatUnreadMessagesCountBackground
color: Theme . highlightBackgroundColor
anchors.fill: parent
radius: width / 2
visible: chatUnreadMessagesItem . visible
}
2020-08-29 22:39:57 +03:00
2021-02-14 23:57:48 +03:00
Text {
id: chatUnreadMessagesCount
font.pixelSize: Theme . fontSizeMedium
font.bold: true
color: Theme . primaryColor
anchors.centerIn: chatUnreadMessagesCountBackground
visible: chatUnreadMessagesItem . visible
2023-02-05 23:44:02 +03:00
text: Functions . formatUnreadCount ( chatInformation . unread_count )
2021-02-14 23:57:48 +03:00
}
MouseArea {
anchors.fill: parent
onClicked: {
chatView . scrollToIndex ( chatView . count - 1 - chatInformation . unread_count )
}
2020-09-22 20:26:49 +03:00
}
}
2020-10-12 23:44:21 +03:00
2021-02-14 23:57:48 +03:00
Loader {
id: stickerPickerLoader
active: false
asynchronous: true
width: parent . width
height: active ? parent.height : 0
source: "../components/StickerPicker.qml"
}
2020-10-16 00:43:55 +03:00
2021-02-14 23:57:48 +03:00
Connections {
target: stickerPickerLoader . item
onStickerPicked: {
Debug . log ( "Sticker picked: " + stickerId ) ;
2022-01-08 01:00:28 +03:00
stickerManager . setNeedsReload ( true ) ;
2021-02-14 23:57:48 +03:00
tdLibWrapper . sendStickerMessage ( chatInformation . id , stickerId , newMessageColumn . replyToMessageId ) ;
stickerPickerLoader . active = false ;
attachmentOptionsFlickable . isNeeded = false ;
newMessageInReplyToRow . inReplyToMessage = null ;
newMessageColumn . editMessageId = "0" ;
}
2020-11-29 07:28:31 +03:00
}
2021-02-14 23:57:48 +03:00
Loader {
id: messageOverlayLoader
2020-11-17 14:18:46 +03:00
2021-02-14 23:57:48 +03:00
property var overlayMessage ;
2020-11-17 14:18:46 +03:00
2021-02-14 23:57:48 +03:00
active: false
asynchronous: true
width: parent . width
height: active ? parent.height : 0
sourceComponent: Component {
MessageOverlayFlickable {
overlayMessage: messageOverlayLoader . overlayMessage
showHeader: ! chatPage . isChannel
onRequestClose: {
messageOverlayLoader . active = false ;
}
2020-11-17 14:18:46 +03:00
}
}
}
2021-02-14 23:57:48 +03:00
Loader {
id: voiceNoteOverlayLoader
active: false
asynchronous: true
width: parent . width
height: active ? parent.height : 0
source: "../components/VoiceNoteOverlay.qml"
onActiveChanged: {
if ( ! active ) {
fernschreiberUtils . stopRecordingVoiceNote ( ) ;
}
2021-01-02 02:15:25 +03:00
}
}
2020-12-31 21:12:50 +03:00
2021-02-14 23:57:48 +03:00
Loader {
id: stickerSetOverlayLoader
2021-02-12 01:39:56 +03:00
2021-02-14 23:57:48 +03:00
property string stickerSetId ;
2021-02-12 01:39:56 +03:00
2021-02-14 23:57:48 +03:00
active: false
asynchronous: true
width: parent . width
height: active ? parent.height : 0
sourceComponent: Component {
StickerSetOverlay {
stickerSetId: stickerSetOverlayLoader . stickerSetId
onRequestClose: {
stickerSetOverlayLoader . active = false ;
}
2021-02-12 01:39:56 +03:00
}
}
2021-02-14 23:57:48 +03:00
onActiveChanged: {
if ( active ) {
attachmentOptionsFlickable . isNeeded = false ;
}
2021-02-13 02:34:01 +03:00
}
2021-02-12 01:39:56 +03:00
}
2021-02-14 23:57:48 +03:00
InlineQuery {
id: inlineQuery
textField: newMessageTextField
chatId: chatInformation . id
}
2021-01-10 04:06:41 +03:00
}
2020-08-21 15:26:56 +03:00
2021-02-14 23:57:48 +03:00
Column {
id: newMessageColumn
spacing: Theme . paddingSmall
topPadding: Theme . paddingSmall + inlineQuery . buttonPadding
anchors.horizontalCenter: parent . horizontalCenter
visible: height > 0
width: parent . width - ( 2 * Theme . horizontalPageMargin )
height: isNeeded ? implicitHeight : 0
Behavior on height { SmoothedAnimation { duration: 200 } }
2020-08-29 12:22:18 +03:00
2021-02-14 23:57:48 +03:00
readonly property bool isNeeded: ! chatPage . isSelecting && chatPage . canSendMessages
property string replyToMessageId: "0" ;
property string editMessageId: "0" ;
2020-10-18 22:54:15 +03:00
2021-02-14 23:57:48 +03:00
InReplyToRow {
onInReplyToMessageChanged: {
if ( inReplyToMessage ) {
newMessageColumn . replyToMessageId = newMessageInReplyToRow . inReplyToMessage . id . toString ( )
newMessageInReplyToRow . visible = true ;
} else {
newMessageInReplyToRow . visible = false ;
newMessageColumn . replyToMessageId = "0" ;
}
}
2020-10-18 22:54:15 +03:00
2021-02-14 23:57:48 +03:00
editable: true
2020-08-21 15:26:56 +03:00
2021-02-14 23:57:48 +03:00
onClearRequested: {
newMessageInReplyToRow . inReplyToMessage = null ;
}
2021-01-02 20:08:33 +03:00
2021-02-14 23:57:48 +03:00
id: newMessageInReplyToRow
myUserId: chatPage . myUserId
visible: false
2021-01-04 00:32:26 +03:00
}
2021-02-14 23:57:48 +03:00
Flickable {
id: attachmentOptionsFlickable
property bool isNeeded: false
width: chatPage . width
x: - Theme . horizontalPageMargin
height: isNeeded && ! inlineQuery . userNameIsValid ? attachmentOptionsRow.height : 0
Behavior on height { SmoothedAnimation { duration: 200 } }
visible: height > 0
contentHeight: attachmentOptionsRow . height
contentWidth: Math . max ( width , attachmentOptionsRow . width )
property bool fadeRight: ( attachmentOptionsRow . width - contentX ) > width
property bool fadeLeft: ! fadeRight && contentX > 0
layer.enabled: fadeRight || fadeLeft
layer.effect: OpacityRampEffectBase {
direction: attachmentOptionsFlickable . fadeRight ? OpacityRamp.LeftToRight : OpacityRamp . RightToLeft
source: attachmentOptionsFlickable
slope: 1 + 6 * ( chatPage . width ) / Screen . width
offset: 1 - 1 / slope
}
2021-01-02 20:08:33 +03:00
2021-02-14 23:57:48 +03:00
Row {
id: attachmentOptionsRow
2021-01-02 20:08:33 +03:00
2021-02-14 23:57:48 +03:00
height: attachImageIconButton . height
2021-01-02 20:08:33 +03:00
2021-02-14 23:57:48 +03:00
anchors.right: parent . right
layoutDirection: Qt . RightToLeft
spacing: Theme . paddingMedium
leftPadding: Theme . horizontalPageMargin
rightPadding: Theme . horizontalPageMargin
IconButton {
id: attachImageIconButton
2023-11-19 15:34:31 +03:00
visible: chatPage . hasSendPrivilege ( "can_send_photos" )
2021-02-14 23:57:48 +03:00
icon.source: "image://theme/icon-m-image"
onClicked: {
var picker = pageStack . push ( "Sailfish.Pickers.ImagePickerPage" , {
allowedOrientations: chatPage . allowedOrientations
} )
picker . selectedContentPropertiesChanged . connect ( function ( ) {
attachmentOptionsFlickable . isNeeded = false ;
Debug . log ( "Selected document: " , picker . selectedContentProperties . filePath ) ;
attachmentPreviewRow . fileProperties = picker . selectedContentProperties ;
attachmentPreviewRow . isPicture = true ;
controlSendButton ( ) ;
} )
}
2021-01-02 20:08:33 +03:00
}
2021-02-14 23:57:48 +03:00
IconButton {
2023-11-19 15:34:31 +03:00
visible: chatPage . hasSendPrivilege ( "can_send_videos" )
2021-02-14 23:57:48 +03:00
icon.source: "image://theme/icon-m-video"
onClicked: {
var picker = pageStack . push ( "Sailfish.Pickers.VideoPickerPage" , {
allowedOrientations: chatPage . allowedOrientations
} )
picker . selectedContentPropertiesChanged . connect ( function ( ) {
attachmentOptionsFlickable . isNeeded = false ;
Debug . log ( "Selected video: " , picker . selectedContentProperties . filePath ) ;
attachmentPreviewRow . fileProperties = picker . selectedContentProperties ;
attachmentPreviewRow . isVideo = true ;
controlSendButton ( ) ;
} )
}
2021-01-02 20:08:33 +03:00
}
2021-02-14 23:57:48 +03:00
IconButton {
2023-11-19 15:34:31 +03:00
visible: chatPage . hasSendPrivilege ( "can_send_voice_notes" )
2021-02-14 23:57:48 +03:00
icon.source: "image://theme/icon-m-mic"
icon . sourceSize {
width: Theme . iconSizeMedium
height: Theme . iconSizeMedium
}
highlighted: down || voiceNoteOverlayLoader . active
onClicked: {
voiceNoteOverlayLoader . active = ! voiceNoteOverlayLoader . active ;
stickerPickerLoader . active = false ;
}
2021-01-02 20:08:33 +03:00
}
2021-02-14 23:57:48 +03:00
IconButton {
2023-11-19 15:34:31 +03:00
visible: chatPage . hasSendPrivilege ( "can_send_documents" )
2021-02-14 23:57:48 +03:00
icon.source: "image://theme/icon-m-document"
onClicked: {
var picker = pageStack . push ( "Sailfish.Pickers.FilePickerPage" , {
allowedOrientations: chatPage . allowedOrientations
} )
picker . selectedContentPropertiesChanged . connect ( function ( ) {
attachmentOptionsFlickable . isNeeded = false ;
Debug . log ( "Selected document: " , picker . selectedContentProperties . filePath ) ;
attachmentPreviewRow . fileProperties = picker . selectedContentProperties ;
attachmentPreviewRow . isDocument = true ;
controlSendButton ( ) ;
} )
}
2021-01-02 20:08:33 +03:00
}
2021-02-14 23:57:48 +03:00
IconButton {
visible: chatPage . hasSendPrivilege ( "can_send_other_messages" )
icon.source: "../../images/icon-m-sticker.svg"
icon . sourceSize {
width: Theme . iconSizeMedium
height: Theme . iconSizeMedium
}
highlighted: down || stickerPickerLoader . active
onClicked: {
stickerPickerLoader . active = ! stickerPickerLoader . active ;
voiceNoteOverlayLoader . active = false ;
}
2021-01-02 20:08:33 +03:00
}
2021-02-14 23:57:48 +03:00
IconButton {
visible: ! ( chatPage . isPrivateChat || chatPage . isSecretChat ) && chatPage . hasSendPrivilege ( "can_send_polls" )
icon.source: "image://theme/icon-m-question"
onClicked: {
pageStack . push ( Qt . resolvedUrl ( "../pages/PollCreationPage.qml" ) , { "chatId" : chatInformation . id , groupName: chatInformation . title } ) ;
attachmentOptionsFlickable . isNeeded = false ;
}
2021-01-03 03:22:30 +03:00
}
2021-02-14 23:57:48 +03:00
IconButton {
visible: fernschreiberUtils . supportsGeoLocation ( ) && newMessageTextField . text === ""
icon.source: "image://theme/icon-m-location"
icon . sourceSize {
width: Theme . iconSizeMedium
height: Theme . iconSizeMedium
}
onClicked: {
fernschreiberUtils . startGeoLocationUpdates ( ) ;
attachmentOptionsFlickable . isNeeded = false ;
attachmentPreviewRow . isLocation = true ;
attachmentPreviewRow . attachmentDescription = qsTr ( "Location: Obtaining position..." ) ;
controlSendButton ( ) ;
}
2021-01-03 03:22:30 +03:00
}
}
2021-02-14 23:57:48 +03:00
2020-10-23 11:29:50 +03:00
}
2021-01-02 20:08:33 +03:00
2020-09-27 14:49:06 +03:00
2021-02-14 23:57:48 +03:00
Row {
id: attachmentPreviewRow
2022-04-24 15:39:36 +03:00
visible: ( ! ! locationData || ! ! fileProperties || isVoiceNote ) && ! inlineQuery . userNameIsValid
2021-02-14 23:57:48 +03:00
spacing: Theme . paddingMedium
width: parent . width
layoutDirection: Qt . RightToLeft
anchors.right: parent . right
2021-01-02 20:08:33 +03:00
2021-02-14 23:57:48 +03:00
property bool isPicture: false ;
property bool isVideo: false ;
property bool isDocument: false ;
property bool isVoiceNote: false ;
property bool isLocation: false ;
property var locationData: null ;
2021-05-28 00:48:50 +03:00
property var geocodedAddress: qsTr ( "Unknown address" )
2021-02-14 23:57:48 +03:00
property var fileProperties: null ;
property string attachmentDescription: "" ;
2020-09-29 00:08:22 +03:00
2021-05-28 00:48:50 +03:00
function getLocationDescription ( ) {
return qsTr ( "Location (%1/%2)" ) . arg ( attachmentPreviewRow . locationData . latitude ) . arg ( attachmentPreviewRow . locationData . longitude ) + " | "
+ qsTr ( "Accuracy: %1m" ) . arg ( attachmentPreviewRow . locationData . horizontalAccuracy ) + "\n"
+ attachmentPreviewRow . geocodedAddress ;
}
2021-02-14 23:57:48 +03:00
Connections {
target: fernschreiberUtils
onNewPositionInformation: {
attachmentPreviewRow . locationData = positionInformation ;
if ( attachmentPreviewRow . isLocation ) {
2021-05-28 00:48:50 +03:00
attachmentPreviewRow . attachmentDescription = attachmentPreviewRow . getLocationDescription ( ) ;
}
}
onNewGeocodedAddress: {
attachmentPreviewRow . geocodedAddress = geocodedAddress ;
if ( attachmentPreviewRow . isLocation ) {
attachmentPreviewRow . attachmentDescription = attachmentPreviewRow . getLocationDescription ( ) ;
2021-02-14 23:57:48 +03:00
}
2021-01-03 03:22:30 +03:00
}
}
2021-02-14 23:57:48 +03:00
IconButton {
id: removeAttachmentsIconButton
icon.source: "image://theme/icon-m-clear"
onClicked: {
clearAttachmentPreviewRow ( ) ;
controlSendButton ( ) ;
}
2020-09-29 00:08:22 +03:00
}
2020-09-27 14:49:06 +03:00
2021-02-14 23:57:48 +03:00
Thumbnail {
id: attachmentPreviewImage
width: Theme . itemSizeMedium
height: Theme . itemSizeMedium
sourceSize.width: width
sourceSize.height: height
fillMode: Thumbnail . PreserveAspectCrop
mimeType: ! ! attachmentPreviewRow . fileProperties ? attachmentPreviewRow . fileProperties . mimeType || "" : ""
source: ! ! attachmentPreviewRow . fileProperties ? attachmentPreviewRow . fileProperties . url || "" : ""
visible: attachmentPreviewRow . isPicture || attachmentPreviewRow . isVideo
}
2020-09-28 00:24:22 +03:00
2021-02-14 23:57:48 +03:00
Label {
id: attachmentPreviewText
font.pixelSize: Theme . fontSizeSmall
text: ( attachmentPreviewRow . isVoiceNote || attachmentPreviewRow . isLocation ) ? attachmentPreviewRow.attachmentDescription : ( ! ! attachmentPreviewRow . fileProperties ? attachmentPreviewRow . fileProperties . fileName || "" : "" ) ;
anchors.verticalCenter: parent . verticalCenter
2020-09-29 00:08:22 +03:00
2021-05-28 00:48:50 +03:00
width: parent . width - removeAttachmentsIconButton . width - Theme . paddingMedium
maximumLineCount: 2
wrapMode: Text . Wrap
2021-02-14 23:57:48 +03:00
truncationMode: TruncationMode . Fade
color: Theme . secondaryColor
visible: attachmentPreviewRow . isDocument || attachmentPreviewRow . isVoiceNote || attachmentPreviewRow . isLocation
}
2020-09-27 14:49:06 +03:00
}
2020-09-30 00:37:56 +03:00
2021-02-14 23:57:48 +03:00
Row {
id: uploadStatusRow
visible: false
spacing: Theme . paddingMedium
width: parent . width
anchors.right: parent . right
2020-09-30 00:37:56 +03:00
2021-02-14 23:57:48 +03:00
Text {
id: uploadingText
font.pixelSize: Theme . fontSizeSmall
text: qsTr ( "Uploading..." )
anchors.verticalCenter: parent . verticalCenter
color: Theme . secondaryColor
visible: uploadStatusRow . visible
}
2020-09-30 00:37:56 +03:00
2021-02-14 23:57:48 +03:00
ProgressBar {
id: uploadingProgressBar
minimumValue: 0
maximumValue: 100
value: 0
visible: uploadStatusRow . visible
width: parent . width - uploadingText . width - Theme . paddingMedium
}
2020-09-30 00:37:56 +03:00
2021-02-14 23:57:48 +03:00
}
2020-10-18 19:57:01 +03:00
2021-02-14 23:57:48 +03:00
Column {
id: emojiColumn
2020-10-18 19:57:01 +03:00
width: parent . width
anchors.horizontalCenter: parent . horizontalCenter
2021-02-14 23:57:48 +03:00
visible: emojiProposals ? ( emojiProposals . length > 0 ? true : false ) : false
opacity: emojiProposals ? ( emojiProposals . length > 0 ? 1 : 0 ) : 0
Behavior on opacity { NumberAnimation { } }
spacing: Theme . paddingMedium
2020-10-18 19:57:01 +03:00
2021-02-14 23:57:48 +03:00
Flickable {
width: parent . width
height: emojiResultRow . height + Theme . paddingSmall
anchors.horizontalCenter: parent . horizontalCenter
contentWidth: emojiResultRow . width
clip: true
Row {
id: emojiResultRow
spacing: Theme . paddingMedium
Repeater {
model: emojiProposals
Item {
height: singleEmojiRow . height
width: singleEmojiRow . width
Row {
id: singleEmojiRow
spacing: Theme . paddingSmall
Image {
id: emojiPicture
2024-02-02 00:08:45 +03:00
source: "../js/emoji/" + modelData . file_name + ".svg"
2021-02-14 23:57:48 +03:00
width: Theme . fontSizeLarge
height: Theme . fontSizeLarge
}
}
2020-10-18 19:57:01 +03:00
2021-02-14 23:57:48 +03:00
MouseArea {
anchors.fill: parent
onClicked: {
replaceMessageText ( newMessageTextField . text , newMessageTextField . cursorPosition , modelData . emoji ) ;
emojiProposals = null ;
}
2020-10-18 19:57:01 +03:00
}
}
2021-02-14 23:57:48 +03:00
}
2020-10-18 19:57:01 +03:00
}
}
}
2021-02-14 23:57:48 +03:00
Column {
id: atMentionColumn
2020-11-29 01:00:10 +03:00
width: parent . width
anchors.horizontalCenter: parent . horizontalCenter
2021-02-14 23:57:48 +03:00
visible: opacity > 0
opacity: knownUsersRepeater . count > 0 ? 1 : 0
Behavior on opacity { NumberAnimation { } }
height: knownUsersRepeater . count > 0 ? childrenRect.height : 0
Behavior on height { SmoothedAnimation { duration: 200 } }
spacing: Theme . paddingMedium
2020-11-29 01:00:10 +03:00
2021-02-14 23:57:48 +03:00
Flickable {
width: parent . width
height: atMentionResultRow . height + Theme . paddingSmall
anchors.horizontalCenter: parent . horizontalCenter
contentWidth: atMentionResultRow . width
clip: true
Row {
id: atMentionResultRow
spacing: Theme . paddingMedium
Repeater {
id: knownUsersRepeater
Item {
id: knownUserItem
height: singleAtMentionRow . height
width: singleAtMentionRow . width
property string atMentionText: "@" + ( user_name ? user_name : user_id + "(" + title + ")" ) ;
Row {
id: singleAtMentionRow
spacing: Theme . paddingSmall
Item {
width: Theme . fontSizeHuge
height: Theme . fontSizeHuge
anchors.verticalCenter: parent . verticalCenter
ProfileThumbnail {
id: atMentionThumbnail
replacementStringHint: title
width: parent . width
height: parent . width
photoData: photo_small
}
2020-11-29 01:00:10 +03:00
}
2021-02-14 23:57:48 +03:00
Column {
Text {
text: Emoji . emojify ( title , Theme . fontSizeExtraSmall )
textFormat: Text . StyledText
color: Theme . primaryColor
font.pixelSize: Theme . fontSizeExtraSmall
font.bold: true
}
Text {
id: userHandleText
text: user_handle
textFormat: Text . StyledText
color: Theme . primaryColor
font.pixelSize: Theme . fontSizeExtraSmall
}
2020-11-29 01:00:10 +03:00
}
}
2021-02-14 23:57:48 +03:00
MouseArea {
anchors.fill: parent
onClicked: {
replaceMessageText ( newMessageTextField . text , newMessageTextField . cursorPosition , knownUserItem . atMentionText ) ;
knownUsersRepeater . model = undefined ;
}
2020-11-29 01:00:10 +03:00
}
}
2021-02-14 23:57:48 +03:00
}
2020-11-29 01:00:10 +03:00
}
}
}
2021-02-14 23:57:48 +03:00
Row {
width: parent . width
spacing: Theme . paddingSmall
visible: newMessageColumn . editMessageId !== "0"
2020-10-18 22:54:15 +03:00
2021-02-14 23:57:48 +03:00
Text {
width: parent . width - Theme . paddingSmall - removeEditMessageIconButton . width
2020-10-18 22:54:15 +03:00
2021-02-14 23:57:48 +03:00
anchors.verticalCenter: parent . verticalCenter
2020-10-18 22:54:15 +03:00
2021-02-14 23:57:48 +03:00
id: editMessageText
font.pixelSize: Theme . fontSizeSmall
font.bold: true
text: qsTr ( "Edit Message" )
color: Theme . secondaryColor
}
2020-10-18 22:54:15 +03:00
2021-02-14 23:57:48 +03:00
IconButton {
id: removeEditMessageIconButton
icon.source: "image://theme/icon-m-clear"
onClicked: {
newMessageColumn . editMessageId = "0" ;
newMessageTextField . text = "" ;
}
2020-10-18 22:54:15 +03:00
}
}
2020-09-19 00:43:23 +03:00
2021-02-14 23:57:48 +03:00
Row {
id: newMessageRow
width: parent . width
anchors.horizontalCenter: parent . horizontalCenter
2020-08-28 18:40:25 +03:00
2021-02-14 23:57:48 +03:00
TextArea {
id: newMessageTextField
width: parent . width - ( attachmentIconButton . visible ? attachmentIconButton.width : 0 ) - ( newMessageSendButton . visible ? newMessageSendButton.width : 0 ) - ( cancelInlineQueryButton . visible ? cancelInlineQueryButton.width : 0 )
height: Math . min ( chatContainer . height / 3 , implicitHeight )
anchors.verticalCenter: parent . verticalCenter
font.pixelSize: Theme . fontSizeSmall
placeholderText: qsTr ( "Your message" )
labelVisible: false
textLeftMargin: 0
textTopMargin: 0
enabled: ! attachmentPreviewRow . isLocation
2021-05-12 21:23:31 +03:00
focus: appSettings . focusTextAreaOnChatOpen
2021-02-14 23:57:48 +03:00
EnterKey.onClicked: {
if ( appSettings . sendByEnter ) {
2021-05-03 20:46:16 +03:00
var messageText = newMessageTextField . text ;
newMessageTextField . text = messageText . substring ( 0 , newMessageTextField . cursorPosition - 1 ) + messageText . substring ( newMessageTextField . cursorPosition ) ;
2021-02-14 23:57:48 +03:00
sendMessage ( ) ;
newMessageTextField . text = "" ;
if ( ! appSettings . focusTextAreaAfterSend ) {
newMessageTextField . focus = false ;
}
2021-01-01 03:30:23 +03:00
}
2020-09-24 00:41:17 +03:00
}
2021-02-14 23:57:48 +03:00
EnterKey.enabled: ! inlineQuery . userNameIsValid && ( ! appSettings . sendByEnter || text . length )
EnterKey.iconSource: appSettings . sendByEnter ? "image://theme/icon-m-chat" : "image://theme/icon-m-enter"
2020-09-16 23:04:02 +03:00
2021-02-14 23:57:48 +03:00
onTextChanged: {
controlSendButton ( ) ;
textReplacementTimer . restart ( ) ;
2020-09-27 14:49:06 +03:00
}
2021-02-21 02:26:39 +03:00
onActiveFocusChanged: {
if ( activeFocus ) {
messageOptionsDrawer . open = false
}
}
2020-09-24 00:41:17 +03:00
}
2021-02-14 23:57:48 +03:00
IconButton {
id: attachmentIconButton
icon.source: "image://theme/icon-m-attach?" + ( attachmentOptionsFlickable . isNeeded ? Theme.highlightColor : Theme . primaryColor )
anchors.bottom: parent . bottom
anchors.bottomMargin: Theme . paddingSmall
enabled: ! attachmentPreviewRow . visible && ! stickerSetOverlayLoader . item
visible: ! inlineQuery . userNameIsValid
onClicked: {
if ( attachmentOptionsFlickable . isNeeded ) {
attachmentOptionsFlickable . isNeeded = false ;
stickerPickerLoader . active = false ;
voiceNoteOverlayLoader . active = false ;
} else {
attachmentOptionsFlickable . isNeeded = true ;
}
2021-01-01 03:30:23 +03:00
}
2020-08-21 15:26:56 +03:00
}
2021-01-10 04:06:41 +03:00
IconButton {
2021-02-14 23:57:48 +03:00
id: newMessageSendButton
icon.source: "image://theme/icon-m-chat"
anchors.bottom: parent . bottom
anchors.bottomMargin: Theme . paddingSmall
visible: ! inlineQuery . userNameIsValid && ( ! appSettings . sendByEnter || attachmentPreviewRow . visible )
enabled: false
2021-01-10 04:06:41 +03:00
onClicked: {
2021-02-14 23:57:48 +03:00
sendMessage ( ) ;
newMessageTextField . text = "" ;
if ( ! appSettings . focusTextAreaAfterSend ) {
newMessageTextField . focus = false ;
2021-01-10 04:06:41 +03:00
}
}
}
2021-02-14 23:57:48 +03:00
Item {
width: cancelInlineQueryButton . width
height: cancelInlineQueryButton . height
visible: inlineQuery . userNameIsValid
anchors.bottom: parent . bottom
anchors.bottomMargin: Theme . paddingSmall
IconButton {
id: cancelInlineQueryButton
icon.source: "image://theme/icon-m-cancel"
visible: parent . visible
opacity: inlineQuery . isLoading ? 0.2 : 1
Behavior on opacity { FadeAnimation { } }
onClicked: {
if ( inlineQuery . query !== "" ) {
newMessageTextField . text = "@" + inlineQuery . userName + " "
newMessageTextField . cursorPosition = newMessageTextField . text . length
lostFocusTimer . start ( ) ;
} else {
newMessageTextField . text = ""
}
}
onPressAndHold: {
newMessageTextField . text = ""
}
}
BusyIndicator {
size: BusyIndicatorSize . Small
anchors.centerIn: parent
running: inlineQuery . isLoading
}
2021-01-10 04:06:41 +03:00
}
2021-02-14 23:57:48 +03:00
}
2020-08-21 15:26:56 +03:00
}
}
2020-12-06 17:50:03 +03:00
}
2021-02-14 23:57:48 +03:00
2020-12-06 17:50:03 +03:00
}
2020-11-18 16:22:08 +03:00
2021-02-14 23:57:48 +03:00
2020-12-06 17:50:03 +03:00
Loader {
id: selectedMessagesActions
asynchronous: true
anchors.bottom: parent . bottom
readonly property bool isNeeded: chatPage . isSelecting
active: height > 0
width: parent . width
height: isNeeded ? Theme.itemSizeMedium : 0
Behavior on height { SmoothedAnimation { duration: 200 } }
sourceComponent: Component {
Item {
clip: true
2020-11-15 01:50:12 +03:00
2020-12-06 17:50:03 +03:00
IconButton {
anchors {
left: parent . left
leftMargin: Theme . horizontalPageMargin
verticalCenter: parent . verticalCenter
}
icon.source: "image://theme/icon-m-cancel"
onClicked: {
chatPage . selectedMessages = [ ] ;
}
}
2020-11-15 01:50:12 +03:00
2020-12-06 17:50:03 +03:00
Row {
spacing: Theme . paddingSmall
anchors {
right: parent . right
rightMargin: Theme . horizontalPageMargin
verticalCenter: parent . verticalCenter
}
2020-11-15 01:50:12 +03:00
2020-12-06 17:50:03 +03:00
IconButton {
icon.source: "../../images/icon-m-copy.svg"
icon.sourceSize: Qt . size ( Theme . iconSizeMedium , Theme . iconSizeMedium )
onClicked: {
Clipboard . text = Functions . getMessagesArrayText ( chatPage . selectedMessages ) ;
appNotification . show ( qsTr ( "%Ln messages have been copied" , "" , selectedMessages . length ) ) ;
chatPage . selectedMessages = [ ] ;
}
}
2020-11-15 01:50:12 +03:00
2020-12-06 17:50:03 +03:00
IconButton {
visible: ! chatPage . isSecretChat && selectedMessages . every ( function ( message ) {
return message . can_be_forwarded
} )
icon.sourceSize: Qt . size ( Theme . iconSizeMedium , Theme . iconSizeMedium )
icon.source: "image://theme/icon-m-forward"
onClicked: {
2021-02-18 01:48:08 +03:00
startForwardingMessages ( chatPage . selectedMessages ) ;
2020-11-15 01:50:12 +03:00
}
2020-12-06 17:50:03 +03:00
}
IconButton {
icon.source: "image://theme/icon-m-delete"
visible: chatInformation . id === chatPage . myUserId || selectedMessages . every ( function ( message ) {
return message . can_be_deleted_for_all_users
} )
icon.sourceSize: Qt . size ( Theme . iconSizeMedium , Theme . iconSizeMedium )
onClicked: {
var ids = Functions . getMessagesArrayIds ( selectedMessages ) ;
var chatId = chatInformation . id
var wrapper = tdLibWrapper ;
Remorse . popupAction ( chatPage , qsTr ( "%Ln Messages deleted" , "" , ids . length ) , function ( ) {
wrapper . deleteMessages ( chatId , ids ) ;
} ) ;
chatPage . selectedMessages = [ ] ;
2020-11-15 01:50:12 +03:00
}
}
}
}
2020-08-21 15:26:56 +03:00
}
}
2023-11-23 00:53:17 +03:00
Timer {
id: doubleTapHintTimer
running: true
triggeredOnStart: false
repeat: false
interval: 6000
onTriggered: {
tapHint . visible = false ;
tapHintLabel . visible = false ;
}
}
TapInteractionHint {
id: tapHint
loops: Animation . Infinite
taps: 2
anchors.centerIn: parent
visible: false
}
InteractionHintLabel {
id: tapHintLabel
anchors.bottom: parent . bottom
text: qsTr ( "Double-tap on a message to choose a reaction" )
visible: false
}
2020-08-21 15:26:56 +03:00
}