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-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"
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-08-22 22:43:20 +03:00
property int myUserId: tdLibWrapper . getUserInformation ( ) . id ;
2020-11-06 01:23:37 +03:00
property var chatInformation ;
2020-08-21 19:03:51 +03:00
property bool isPrivateChat: false ;
property bool isBasicGroup: false ;
property bool isSuperGroup: false ;
property bool isChannel: false ;
2020-11-06 01:23:37 +03:00
property var chatPartnerInformation ;
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-07 22:29:44 +03:00
property bool userIsMember: ( isPrivateChat && chatInformation [ "@type" ] ) || // should be optimized
( 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-08-21 19:03:51 +03:00
function updateChatPartnerStatusText ( ) {
2020-10-19 13:20:02 +03:00
var statusText = Functions . getChatPartnerStatusText ( chatPartnerInformation . status [ '@type' ] , chatPartnerInformation . status . was_online ) ;
if ( statusText ) {
chatStatusText . text = statusText ;
2020-08-21 19:03:51 +03:00
}
}
function updateGroupStatusText ( ) {
if ( chatOnlineMemberCount > 0 ) {
2020-09-24 10:55:05 +03:00
chatStatusText . text = qsTr ( "%1 members, %2 online" ) . arg ( Functions . getShortenedCount ( chatGroupInformation . member_count ) ) . arg ( Functions . getShortenedCount ( chatOnlineMemberCount ) ) ;
2020-08-21 19:03:51 +03:00
} else {
if ( isChannel ) {
2020-09-24 10:55:05 +03:00
chatStatusText . text = qsTr ( "%1 subscribers" ) . arg ( Functions . getShortenedCount ( chatGroupInformation . member_count ) ) ;
2020-08-21 19:03:51 +03:00
} else {
2020-09-24 10:55:05 +03:00
chatStatusText . text = qsTr ( "%1 members" ) . 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-08-30 00:59:29 +03:00
console . log ( "[ChatPage] Initializing chat page..." ) ;
2020-08-29 22:39:57 +03:00
chatView . currentIndex = - 1 ;
2020-08-30 20:04:16 +03:00
chatView . lastReadSentIndex = 0 ;
2020-08-21 19:03:51 +03:00
var chatType = chatInformation . type [ '@type' ] ;
isPrivateChat = ( chatType === "chatTypePrivate" ) ;
isBasicGroup = ( chatType === "chatTypeBasicGroup" ) ;
isSuperGroup = ( chatType === "chatTypeSupergroup" ) ;
if ( isPrivateChat ) {
chatPartnerInformation = tdLibWrapper . getUserInformation ( chatInformation . type . user_id ) ;
updateChatPartnerStatusText ( ) ;
}
if ( isBasicGroup ) {
chatGroupInformation = tdLibWrapper . getBasicGroup ( chatInformation . type . basic_group_id ) ;
updateGroupStatusText ( ) ;
}
if ( isSuperGroup ) {
chatGroupInformation = tdLibWrapper . getSuperGroup ( chatInformation . type . supergroup_id ) ;
isChannel = chatGroupInformation . is_channel ;
updateGroupStatusText ( ) ;
}
2020-10-17 00:00:08 +03:00
if ( stickerManager . needsReload ( ) ) {
console . log ( "Stickers will be reloaded!" ) ;
tdLibWrapper . getRecentStickers ( ) ;
tdLibWrapper . getInstalledStickerSets ( ) ;
stickerManager . setNeedsReload ( false ) ;
}
2020-08-21 15:26:56 +03:00
}
2020-11-03 01:42:23 +03:00
function getMessageStatusText ( message , listItemIndex , lastReadSentIndex , useElapsed ) {
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
if ( message . edit_date > 0 ) {
2020-09-20 01:13:42 +03:00
messageStatusSuffix += " - " + qsTr ( "edited" ) ;
2020-09-19 21:33:51 +03:00
}
2020-08-31 22:51:52 +03:00
if ( chatPage . myUserId === message . sender_user_id ) {
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 . visible = false ;
attachmentPreviewRow . isPicture = false ;
2020-09-28 00:24:22 +03:00
attachmentPreviewRow . isVideo = false ;
attachmentPreviewRow . isDocument = false ;
2020-09-29 00:08:22 +03:00
attachmentPreviewRow . fileProperties = { } ;
}
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
|| attachmentPreviewRow . isVideo ) {
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
}
2020-09-27 14:49:06 +03:00
clearAttachmentPreviewRow ( ) ;
} else {
tdLibWrapper . sendTextMessage ( chatInformation . id , newMessageTextField . text , newMessageColumn . replyToMessageId ) ;
}
}
2020-09-29 22:00:23 +03:00
controlSendButton ( ) ;
2020-10-18 22:54:15 +03:00
newMessageInReplyToRow . inReplyToMessage = null ;
newMessageColumn . editMessageId = "0" ;
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 ) {
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 ;
}
}
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 ( ) ;
}
2020-08-21 15:26:56 +03:00
Component.onCompleted: {
initializePage ( ) ;
}
2020-10-19 13:20:02 +03:00
Component.onDestruction: {
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 ) ;
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 ) ;
chatPage . isInitialized = true ;
}
2020-10-19 13:20:02 +03:00
break ;
2020-08-29 19:04:23 +03:00
}
2020-10-19 13:20:02 +03:00
2020-08-21 15:47:08 +03:00
}
2020-08-21 19:03:51 +03:00
Connections {
target: tdLibWrapper
onUserUpdated: {
if ( isPrivateChat && chatPartnerInformation . id . toString ( ) === userId ) {
chatPartnerInformation = userInformation ;
updateChatPartnerStatusText ( ) ;
}
}
onBasicGroupUpdated: {
if ( isBasicGroup && chatGroupInformation . id . toString ( ) === groupId ) {
chatGroupInformation = groupInformation ;
updateGroupStatusText ( ) ;
}
}
onSuperGroupUpdated: {
if ( isSuperGroup && chatGroupInformation . id . toString ( ) === groupId ) {
chatGroupInformation = groupInformation ;
updateGroupStatusText ( ) ;
}
}
onChatOnlineMemberCountUpdated: {
console . log ( isSuperGroup + "/" + isBasicGroup + "/" + chatInformation . id . toString ( ) + "/" + chatId ) ;
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 ;
uploadingProgressBar . maximumValue = fileInformation . size ;
uploadingProgressBar . value = fileInformation . remote . uploaded_size ;
}
2020-10-18 19:57:01 +03:00
onEmojiSearchSuccessful: {
chatPage . emojiProposals = result ;
}
2020-08-21 19:03:51 +03:00
}
2020-08-22 22:43:20 +03:00
Connections {
target: chatModel
onMessagesReceived: {
2020-08-30 20:04:16 +03:00
console . log ( "[ChatPage] Messages received, view has " + chatView . count + " messages, setting view to index " + modelIndex + ", own messages were read before index " + lastReadSentIndex ) ;
2020-10-19 13:20:02 +03:00
if ( totalCount === 0 ) {
console . log ( "[ChatPage] actually, skipping that: No Messages in Chat." ) ;
chatView . positionViewAtEnd ( ) ;
chatPage . loading = false ;
return ;
}
2020-08-30 20:04:16 +03:00
chatView . lastReadSentIndex = lastReadSentIndex ;
2020-11-02 22:54:18 +03:00
chatView . scrollToIndex ( modelIndex ) ;
2020-09-14 00:25:48 +03:00
chatPage . loading = false ;
2020-09-16 22:12:39 +03:00
if ( chatView . height > chatView . contentHeight ) {
console . 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-08-22 22:43:20 +03:00
}
2020-08-23 00:49:02 +03:00
onNewMessageReceived: {
2020-11-02 22:54:18 +03:00
if ( chatView . manuallyScrolledToBottom || message . sender_user_id === chatPage . myUserId ) {
console . log ( "[ChatPage] Own message received or was scrolled to bottom, scrolling down to see it..." ) ;
chatView . scrollToIndex ( 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: {
console . log ( "[ChatPage] Unread count updated, new count: " + unreadCount ) ;
chatInformation . unread_count = unreadCount ;
2020-09-15 23:47:39 +03:00
chatUnreadMessagesCountBackground . visible = ( ! chatPage . loading && unreadCount > 0 ) ;
chatUnreadMessagesCount . text = unreadCount > 99 ? "99+" : unreadCount ;
2020-08-29 22:39:57 +03:00
}
2020-08-30 20:04:16 +03:00
onLastReadSentMessageUpdated: {
console . log ( "[ChatPage] Updating last read sent index, new index: " + lastReadSentIndex ) ;
chatView . lastReadSentIndex = lastReadSentIndex ;
}
2020-09-01 23:37:48 +03:00
onMessagesIncrementalUpdate: {
console . log ( "Incremental update received. View now has " + chatView . count + " messages, view is on index " + modelIndex + ", own messages were read before index " + lastReadSentIndex ) ;
chatView . lastReadSentIndex = lastReadSentIndex ;
2020-09-22 22:05:24 +03:00
chatViewCooldownTimer . start ( ) ;
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-08-22 22:43:20 +03:00
}
2020-11-07 22:29:44 +03:00
Connections {
target: chatListModel
onChatJoined: {
chatPageNotification . show ( qsTr ( "You joined the chat %1" ) . arg ( chatTitle ) ) ;
}
}
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-10-19 13:20:02 +03:00
running: isPrivateChat
2020-08-21 19:03:51 +03:00
repeat: true
onTriggered: {
updateChatPartnerStatusText ( ) ;
}
}
2020-11-04 14:26:57 +03:00
Timer {
id: viewMessageTimer
2020-11-05 02:02:27 +03:00
interval: 1000
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: {
if ( chatInformation . unread_count > 0 && lastQueuedIndex > - 1 ) {
var messageToRead = chatModel . getMessage ( lastQueuedIndex ) ;
if ( messageToRead && messageToRead . id ) {
tdLibWrapper . viewMessage ( chatInformation . id , messageToRead . id , false ) ;
}
lastQueuedIndex = - 1
}
}
}
2020-08-21 19:03:51 +03:00
2020-08-21 15:26:56 +03:00
SilicaFlickable {
id: chatContainer
anchors.fill: parent
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
contentHeight: height
contentWidth: width
2020-08-21 15:26:56 +03:00
2020-09-16 21:43:36 +03:00
PullDownMenu {
2020-10-16 00:43:55 +03:00
visible: chatInformation . id !== chatPage . myUserId && ! stickerPickerLoader . active
2020-11-07 22:29:44 +03:00
MenuItem {
id: joinLeaveChatMenuItem
visible: ( chatPage . isSuperGroup || chatPage . isBasicGroup ) && chatGroupInformation && chatGroupInformation . status [ "@type" ] !== "chatMemberStatusBanned"
onClicked: {
if ( chatPage . userIsMember ) {
var remorse = Remorse . popupAction ( appWindow , qsTr ( "Leaving chat" ) , ( function ( chatid ) {
return 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 ) } ) ) ;
} ;
} ( chatInformation . id ) ) )
} else {
tdLibWrapper . joinChat ( chatInformation . id ) ;
}
}
text: chatPage . userIsMember ? qsTr ( "Leave Chat" ) : qsTr ( "Join Chat" )
}
2020-09-16 21:43:36 +03:00
MenuItem {
id: muteChatMenuItem
2020-11-07 22:29:44 +03:00
visible: chatPage . userIsMember
2020-09-16 21:43:36 +03:00
onClicked: {
var newNotificationSettings = chatInformation . notification_settings ;
if ( newNotificationSettings . mute_for > 0 ) {
newNotificationSettings . mute_for = 0 ;
} else {
newNotificationSettings . mute_for = 6666666 ;
}
tdLibWrapper . setChatNotificationSettings ( chatInformation . id , newNotificationSettings ) ;
}
text: chatInformation . notification_settings . mute_for > 0 ? qsTr ( "Unmute Chat" ) : qsTr ( "Mute Chat" )
}
}
2020-11-07 22:29:44 +03:00
AppNotification {
id: chatPageNotification
}
2020-10-19 13:20:02 +03:00
BackgroundItem {
id: headerMouseArea
height: headerRow . height
width: parent . width
onClicked: {
pageStack . push ( Qt . resolvedUrl ( "../pages/ChatInformationPage.qml" ) , { "chatInformation" : chatInformation , "privateChatUserInformation" : chatPartnerInformation , "groupInformation" : chatGroupInformation , "chatOnlineMemberCount" : chatOnlineMemberCount } ) ;
}
}
2020-08-21 15:26:56 +03:00
Column {
id: chatColumn
width: parent . width
height: parent . height
2020-09-19 19:49:11 +03:00
spacing: Theme . paddingSmall
2020-08-21 15:26:56 +03:00
Row {
id: headerRow
width: parent . width - ( 3 * Theme . horizontalPageMargin )
2020-09-18 23:55:59 +03:00
height: chatOverviewColumn . height + Theme . paddingLarge
2020-08-21 15:26:56 +03:00
anchors.horizontalCenter: parent . horizontalCenter
spacing: Theme . paddingMedium
ProfileThumbnail {
id: chatPictureThumbnail
photoData: ( typeof chatInformation . photo !== "undefined" ) ? chatInformation.photo.small : ""
replacementStringHint: chatNameText . text
width: chatOverviewColumn . height
height: chatOverviewColumn . height
2020-09-19 19:49:11 +03:00
anchors.bottom: parent . bottom
anchors.bottomMargin: Theme . paddingSmall
2020-08-21 15:26:56 +03:00
}
Column {
id: chatOverviewColumn
width: parent . width - chatPictureThumbnail . width - Theme . paddingMedium
2020-09-19 19:49:11 +03:00
anchors.bottom: parent . bottom
anchors.bottomMargin: Theme . paddingSmall
2020-08-21 15:26:56 +03:00
Text {
id: chatNameText
text: chatInformation . title !== "" ? Emoji . emojify ( chatInformation . title , font . pixelSize ) : qsTr ( "Unknown" )
textFormat: Text . StyledText
font.pixelSize: Theme . fontSizeLarge
font.family: Theme . fontFamilyHeading
color: Theme . highlightColor
elide: Text . ElideRight
width: parent . width
maximumLineCount: 1
horizontalAlignment: Text . AlignRight
onTruncatedChanged: {
// There is obviously a bug in QML in truncating text with images.
// We simply remove Emojis then...
if ( truncated ) {
text = text . replace ( /\<img [^>]+\/\>/g , "" ) ;
}
}
}
Text {
id: chatStatusText
2020-08-21 19:03:51 +03:00
text: ""
2020-08-21 15:26:56 +03:00
textFormat: Text . StyledText
2020-08-21 19:03:51 +03:00
font.pixelSize: Theme . fontSizeExtraSmall
2020-08-21 15:26:56 +03:00
font.family: Theme . fontFamilyHeading
2020-10-19 13:20:02 +03:00
color: headerMouseArea . pressed ? Theme.secondaryHighlightColor : Theme . secondaryColor
2020-08-21 15:26:56 +03:00
elide: Text . ElideRight
width: parent . width
maximumLineCount: 1
horizontalAlignment: Text . AlignRight
}
}
}
2020-08-29 19:04:23 +03:00
Item {
id: chatViewItem
2020-08-21 15:26:56 +03:00
width: parent . width
2020-09-19 19:49:11 +03:00
height: parent . height - headerRow . height - Theme . paddingSmall - ( chatPage . isChannel ? 0 : ( newMessageColumn . height + Theme . paddingSmall ) )
2020-08-21 15:26:56 +03:00
2020-09-18 23:55:59 +03:00
property int previousHeight ;
Component.onCompleted: {
previousHeight = height ;
}
onHeightChanged: {
var deltaHeight = previousHeight - height ;
chatView . contentY = chatView . contentY + deltaHeight ;
previousHeight = height ;
}
2020-09-22 22:05:24 +03:00
Timer {
id: chatViewCooldownTimer
interval: 2000
repeat: false
running: false
onTriggered: {
console . log ( "[ChatPage] Cooldown completed..." ) ;
chatView . inCooldown = false ;
}
}
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
2020-08-29 19:04:23 +03:00
SilicaListView {
id: chatView
2020-08-21 15:26:56 +03:00
2020-08-29 19:04:23 +03:00
anchors.fill: parent
opacity: chatPage . loading ? 0 : 1
Behavior on opacity { NumberAnimation { } }
clip: true
2020-11-02 22:54:18 +03:00
highlightMoveDuration: 0
highlightResizeDuration: 0
2020-08-30 20:04:16 +03:00
property int lastReadSentIndex: 0
2020-09-22 22:05:24 +03:00
property bool inCooldown: false
2020-11-02 22:54:18 +03:00
property bool manuallyScrolledToBottom
2020-11-07 22:58:23 +03:00
property QtObject precalculatedValues: QtObject {
property alias page: chatPage
property bool showUserInfo: page . isBasicGroup || ( page . isSuperGroup && ! page . isChannel )
property int profileThumbnailDimensions: showUserInfo ? Theme.itemSizeSmall : 0
property int pageMarginDouble: 2 * Theme . horizontalPageMargin
property int paddingMediumDouble: 2 * Theme . paddingMedium
property int entryWidth: chatView . width - pageMarginDouble
property int textItemWidth: entryWidth - profileThumbnailDimensions - Theme . paddingSmall
property int backgroundWidth: textItemWidth - pageMarginDouble
property int backgroundRadius: textItemWidth / 50
property int textColumnWidth: backgroundWidth - Theme . horizontalPageMargin
property int messageInReplyToHeight: Theme . fontSizeExtraSmall * 2.571428571 + Theme . paddingSmall ;
property int webPagePreviewHeight: ( ( textColumnWidth * 2 / 3 ) + ( 6 * Theme . fontSizeExtraSmall ) + ( 7 * Theme . paddingSmall ) )
}
2020-08-30 20:04:16 +03:00
2020-08-29 19:04:23 +03:00
function handleScrollPositionChanged ( ) {
2020-08-30 00:06:14 +03:00
console . log ( "Current position: " + chatView . contentY ) ;
2020-11-02 22:54:18 +03:00
if ( chatInformation . unread_count > 0 ) {
2020-11-04 14:26:57 +03:00
var bottomIndex = chatView . indexAt ( chatView . contentX , ( chatView . contentY + chatView . height - Theme . horizontalPageMargin ) ) ;
if ( bottomIndex > - 1 ) {
viewMessageTimer . queueViewMessage ( bottomIndex )
2020-11-02 22:54:18 +03:00
}
}
manuallyScrolledToBottom = chatView . atYEnd
}
function scrollToIndex ( index ) {
if ( index > 0 && index < chatView . count ) {
2020-11-07 22:58:23 +03:00
positionViewAtIndex ( index , ListView . Contain )
// currentIndex = index;
2020-11-02 22:54:18 +03:00
if ( index === chatView . count - 1 ) {
manuallyScrolledToBottom = true ;
}
}
2020-08-29 19:04:23 +03:00
}
2020-08-29 22:39:57 +03:00
onContentYChanged: {
2020-09-22 23:46:48 +03:00
if ( ! chatPage . loading && ! chatView . inCooldown && chatView . indexAt ( chatView . contentX , chatView . contentY ) < 10 ) {
console . log ( "[ChatPage] Trying to get older history items..." ) ;
chatView . inCooldown = true ;
chatModel . triggerLoadMoreHistory ( ) ;
2020-08-30 16:04:15 +03:00
}
2020-08-29 22:39:57 +03:00
}
2020-08-29 19:04:23 +03:00
onMovementEnded: {
2020-08-26 23:52:06 +03:00
handleScrollPositionChanged ( ) ;
2020-08-23 00:05:45 +03:00
}
2020-08-29 19:04:23 +03:00
onQuickScrollAnimatingChanged: {
if ( ! quickScrollAnimating ) {
handleScrollPositionChanged ( ) ;
2020-11-02 22:54:18 +03:00
if ( atYEnd ) { // handle some false guesses from quick scroll
chatView . scrollToIndex ( chatView . count - 2 )
chatView . scrollToIndex ( chatView . count - 1 )
}
2020-08-29 19:04:23 +03:00
}
}
model: chatModel
2020-11-06 01:23:37 +03:00
property var contentComponentNames: ( {
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
messageSticker: "StickerPreview" ,
messagePhoto: "ImagePreview" ,
messageVideo: "VideoPreview" ,
messageAnimation: "VideoPreview" ,
messageAudio: "AudioPreview" ,
messageVoiceNote: "AudioPreview" ,
messageDocument: "DocumentPreview" ,
messageLocation: "LocationPreview" ,
messageVenue: "LocationPreview" ,
messagePoll: "PollPreview"
} )
2020-10-31 22:02:18 +03:00
function getContentComponentHeight ( componentName , content , parentWidth ) {
switch ( componentName ) {
case "StickerPreview" : return content . sticker . height ;
case "ImagePreview" :
case "LocationPreview" :
return parentWidth * 0.66666666 ; // 2 / 3;
case "VideoPreview" :
return Functions . getVideoHeight ( parentWidth , ( content [ '@type' ] === "messageVideo" ) ? content.video : content . animation ) ;
case "AudioPreview" :
return parentWidth / 2 ;
case "DocumentPreview" :
return Theme . itemSizeSmall ;
case "PollPreview" :
return Theme . itemSizeSmall * ( 4 + content . poll . options ) ;
}
}
2020-08-21 15:26:56 +03:00
2020-11-05 16:05:33 +03:00
property var simpleDelegateMessages: [ "messageBasicGroupChatCreate" , "messageChatAddMembers" , "messageChatChangePhoto" , "messageChatChangeTitle" , "messageChatDeleteMember" , "messageChatDeletePhoto" , "messageChatJoinByLink" , "messageChatSetTtl" , "messageChatUpgradeFrom" , "messageChatUpgradeTo" , "messageCustomServiceAction" , "messagePinMessage" , "messageScreenshotTaken" , "messageSupergroupChatCreate" , "messageUnsupported" ]
delegate: Loader {
width: chatView . width
Component {
id: messageListViewItemComponent
2020-11-07 22:58:23 +03:00
MessageListViewItem {
precalculatedValues: chatView . precalculatedValues
}
2020-09-15 23:47:39 +03:00
}
2020-11-05 16:05:33 +03:00
Component {
id: messageListViewItemSimpleComponent
MessageListViewItemSimple { }
2020-08-22 22:43:20 +03:00
}
2020-11-05 16:05:33 +03:00
sourceComponent: chatView . simpleDelegateMessages . indexOf ( display . content [ '@type' ] ) > - 1 ? messageListViewItemSimpleComponent : messageListViewItemComponent
2020-08-22 22:43:20 +03:00
}
2020-08-29 19:04:23 +03:00
VerticalScrollDecorator { }
2020-10-19 13:20:02 +03:00
ViewPlaceholder {
enabled: chatView . count === 0
text: qsTr ( "This chat is empty." )
}
2020-08-29 19:04:23 +03:00
}
Column {
width: parent . width
height: loadingLabel . height + loadingBusyIndicator . height + Theme . paddingMedium
spacing: Theme . paddingMedium
anchors.verticalCenter: parent . verticalCenter
opacity: chatPage . loading ? 1 : 0
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
Behavior on opacity { FadeAnimation { } }
2020-08-29 19:04:23 +03:00
visible: chatPage . loading
InfoLabel {
id: loadingLabel
text: qsTr ( "Loading messages..." )
}
BusyIndicator {
id: loadingBusyIndicator
anchors.horizontalCenter: parent . horizontalCenter
running: chatPage . loading
size: BusyIndicatorSize . Large
}
2020-08-21 15:26:56 +03:00
}
2020-09-22 20:26:49 +03:00
Item {
id: chatUnreadMessagesItem
2020-08-29 22:39:57 +03:00
width: Theme . fontSizeHuge
height: Theme . fontSizeHuge
anchors.right: parent . right
anchors.rightMargin: Theme . paddingMedium
anchors.bottom: parent . bottom
anchors.bottomMargin: Theme . paddingMedium
2020-09-22 20:26:49 +03:00
Rectangle {
id: chatUnreadMessagesCountBackground
color: Theme . highlightBackgroundColor
anchors.fill: parent
radius: width / 2
visible: ! chatPage . loading && chatInformation . unread_count > 0
}
2020-08-29 22:39:57 +03:00
2020-09-22 20:26:49 +03:00
Text {
id: chatUnreadMessagesCount
font.pixelSize: Theme . fontSizeMedium
font.bold: true
color: Theme . primaryColor
anchors.centerIn: chatUnreadMessagesCountBackground
visible: chatUnreadMessagesCountBackground . visible
text: chatInformation . unread_count > 99 ? "99+" : chatInformation . unread_count
}
MouseArea {
anchors.fill: parent
onClicked: {
2020-11-02 22:54:18 +03:00
chatView . scrollToIndex ( chatView . count - 1 - chatInformation . unread_count )
2020-09-22 20:26:49 +03:00
}
}
2020-08-29 22:39:57 +03:00
}
2020-10-12 23:44:21 +03:00
2020-10-16 00:43:55 +03:00
Loader {
id: stickerPickerLoader
active: false
asynchronous: true
2020-10-16 01:02:58 +03:00
width: parent . width
height: active ? parent.height : 0
2020-10-16 00:43:55 +03:00
source: "../components/StickerPicker.qml"
}
2020-08-21 15:26:56 +03:00
}
2020-08-28 18:40:25 +03:00
Column {
id: newMessageColumn
2020-09-19 19:49:11 +03:00
spacing: Theme . paddingSmall
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
2020-08-28 18:40:25 +03:00
width: parent . width - ( 2 * Theme . horizontalPageMargin )
anchors.horizontalCenter: parent . horizontalCenter
2020-08-30 12:12:13 +03:00
visible: ! chatPage . isChannel
2020-08-28 18:40:25 +03:00
2020-08-29 14:38:28 +03:00
property string replyToMessageId: "0" ;
2020-09-19 00:43:23 +03:00
property string editMessageId: "0" ;
2020-08-29 14:38:28 +03:00
2020-08-28 18:40:25 +03:00
InReplyToRow {
2020-08-29 12:22:18 +03:00
onInReplyToMessageChanged: {
2020-08-29 17:58:48 +03:00
if ( inReplyToMessage ) {
2020-08-29 14:38:28 +03:00
newMessageColumn . replyToMessageId = newMessageInReplyToRow . inReplyToMessage . id . toString ( )
newMessageInReplyToRow . visible = true ;
2020-08-29 12:22:18 +03:00
} else {
2020-08-29 14:38:28 +03:00
newMessageInReplyToRow . visible = false ;
newMessageColumn . replyToMessageId = "0" ;
2020-08-29 12:22:18 +03:00
}
}
2020-10-18 22:54:15 +03:00
editable: true
onClearRequested: {
newMessageInReplyToRow . inReplyToMessage = null ;
}
2020-08-29 12:22:18 +03:00
id: newMessageInReplyToRow
myUserId: chatPage . myUserId
visible: false
2020-08-21 15:26:56 +03:00
}
2020-09-27 14:49:06 +03:00
Row {
id: attachmentOptionsRow
visible: false
anchors.right: parent . right
2020-09-29 00:08:22 +03:00
width: parent . width
layoutDirection: Qt . RightToLeft
2020-09-28 00:24:22 +03:00
spacing: Theme . paddingMedium
IconButton {
2020-09-29 00:08:22 +03:00
id: imageAttachmentButton
icon.source: "image://theme/icon-m-image"
2020-09-28 00:24:22 +03:00
onClicked: {
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
var picker = pageStack . push ( "Sailfish.Pickers.ImagePickerPage" ) ;
picker . selectedContentPropertiesChanged . connect ( function ( ) {
attachmentOptionsRow . visible = false ;
console . log ( "Selected document: " + picker . selectedContentProperties . filePath ) ;
attachmentPreviewRow . fileProperties = picker . selectedContentProperties ;
2020-11-01 19:54:00 +03:00
attachmentPreviewRow . isPicture = true ;
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
attachmentPreviewRow . visible = true ;
controlSendButton ( ) ;
} )
2020-09-28 00:24:22 +03:00
}
}
IconButton {
id: videoAttachmentButton
icon.source: "image://theme/icon-m-video"
onClicked: {
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
var picker = pageStack . push ( "Sailfish.Pickers.VideoPickerPage" ) ;
picker . selectedContentPropertiesChanged . connect ( function ( ) {
attachmentOptionsRow . visible = false ;
console . log ( "Selected video: " + picker . selectedContentProperties . filePath ) ;
attachmentPreviewRow . fileProperties = picker . selectedContentProperties ;
attachmentPreviewRow . isVideo = true ;
attachmentPreviewRow . visible = true ;
controlSendButton ( ) ;
} )
2020-09-28 00:24:22 +03:00
}
}
2020-09-27 14:49:06 +03:00
IconButton {
2020-09-29 00:08:22 +03:00
id: documentAttachmentButton
icon.source: "image://theme/icon-m-document"
2020-09-27 14:49:06 +03:00
onClicked: {
2020-11-01 19:54:00 +03:00
var picker = pageStack . push ( "Sailfish.Pickers.DocumentPickerPage" ) ;
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
picker . selectedContentPropertiesChanged . connect ( function ( ) {
attachmentOptionsRow . visible = false ;
console . log ( "Selected document: " + picker . selectedContentProperties . filePath ) ;
attachmentPreviewRow . fileProperties = picker . selectedContentProperties ;
attachmentPreviewRow . isDocument = true ;
attachmentPreviewRow . visible = true ;
controlSendButton ( ) ;
} )
2020-09-27 14:49:06 +03:00
}
}
2020-10-26 17:15:53 +03:00
IconButton {
icon.source: "../../images/icon-m-sticker.svg"
icon . sourceSize {
width: Theme . iconSizeMedium
height: Theme . iconSizeMedium
}
highlighted: down || stickerPickerLoader . active
onClicked: {
stickerPickerLoader . active = ! stickerPickerLoader . active ;
2020-10-04 16:09:06 +03:00
}
}
2020-10-23 11:29:50 +03:00
IconButton {
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
visible: ! chatPage . isPrivateChat && chatGroupInformation &&
2020-10-26 12:24:50 +03:00
( chatGroupInformation . status [ "@type" ] === "chatMemberStatusCreator"
|| chatGroupInformation . status [ "@type" ] === "chatMemberStatusAdministrator"
|| ( chatGroupInformation . status [ "@type" ] === "chatMemberStatusMember" && chatInformation . permissions . can_send_polls ) )
2020-10-23 11:29:50 +03:00
icon.source: "image://theme/icon-m-question"
onClicked: {
pageStack . push ( Qt . resolvedUrl ( "../pages/PollCreationPage.qml" ) , { "chatId" : chatInformation . id , groupName: chatInformation . title } ) ;
attachmentOptionsRow . visible = false ;
}
}
2020-09-27 14:49:06 +03:00
}
Row {
id: attachmentPreviewRow
visible: false
spacing: Theme . paddingMedium
2020-09-29 00:08:22 +03:00
width: parent . width
layoutDirection: Qt . RightToLeft
2020-09-27 14:49:06 +03:00
anchors.right: parent . right
property bool isPicture: false ;
2020-09-28 00:24:22 +03:00
property bool isVideo: false ;
property bool isDocument: false ;
2020-11-06 01:23:37 +03:00
property var fileProperties: ( { } ) ;
2020-09-29 00:08:22 +03:00
IconButton {
id: removeAttachmentsIconButton
icon.source: "image://theme/icon-m-clear"
onClicked: {
clearAttachmentPreviewRow ( ) ;
2020-09-29 22:00:23 +03:00
controlSendButton ( ) ;
2020-09-29 00:08:22 +03:00
}
}
2020-09-27 14:49:06 +03:00
2020-09-28 00:24:22 +03:00
Thumbnail {
2020-09-27 14:49:06 +03:00
id: attachmentPreviewImage
width: Theme . itemSizeMedium
height: Theme . itemSizeMedium
2020-09-28 00:24:22 +03:00
sourceSize.width: width
sourceSize.height: height
2020-09-27 14:49:06 +03:00
2020-09-28 00:24:22 +03:00
fillMode: Thumbnail . PreserveAspectCrop
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
mimeType: typeof attachmentPreviewRow . fileProperties !== "undefined" ? attachmentPreviewRow . fileProperties . mimeType || "" : ""
source: typeof attachmentPreviewRow . fileProperties !== "undefined" ? attachmentPreviewRow . fileProperties . url || "" : ""
2020-09-28 00:24:22 +03:00
visible: attachmentPreviewRow . isPicture || attachmentPreviewRow . isVideo
}
Text {
id: attachmentPreviewText
font.pixelSize: Theme . fontSizeSmall
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
text: typeof attachmentPreviewRow . fileProperties !== "undefined" ? attachmentPreviewRow . fileProperties . fileName || "" : "" ;
2020-09-29 00:08:22 +03:00
anchors.verticalCenter: parent . verticalCenter
2020-09-28 00:24:22 +03:00
maximumLineCount: 1
elide: Text . ElideRight
color: Theme . secondaryColor
2020-09-29 00:08:22 +03:00
visible: attachmentPreviewRow . isDocument
2020-09-27 14:49:06 +03:00
}
}
2020-09-30 00:37:56 +03:00
Row {
id: uploadStatusRow
visible: false
spacing: Theme . paddingMedium
width: parent . width
anchors.right: parent . right
Text {
id: uploadingText
font.pixelSize: Theme . fontSizeSmall
text: qsTr ( "Uploading..." )
anchors.verticalCenter: parent . verticalCenter
color: Theme . secondaryColor
visible: uploadStatusRow . visible
}
ProgressBar {
id: uploadingProgressBar
minimumValue: 0
maximumValue: 100
value: 0
visible: uploadStatusRow . visible
width: parent . width - uploadingText . width - Theme . paddingMedium
}
}
2020-10-18 19:57:01 +03:00
Column {
id: emojiColumn
width: parent . width
anchors.horizontalCenter: parent . horizontalCenter
visible: emojiProposals ? ( emojiProposals . length > 0 ? true : false ) : false
opacity: emojiProposals ? ( emojiProposals . length > 0 ? 1 : 0 ) : 0
Behavior on opacity { NumberAnimation { } }
spacing: Theme . paddingMedium
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
source: "../js/emoji/" + modelData . file_name
width: Theme . fontSizeLarge
height: Theme . fontSizeLarge
}
}
MouseArea {
anchors.fill: parent
onClicked: {
replaceMessageText ( newMessageTextField . text , newMessageTextField . cursorPosition , modelData . emoji ) ;
emojiProposals = null ;
}
}
}
}
}
}
}
2020-10-18 22:54:15 +03:00
Row {
2020-09-19 00:43:23 +03:00
width: parent . width
2020-10-18 22:54:15 +03:00
spacing: Theme . paddingSmall
2020-09-19 00:43:23 +03:00
visible: newMessageColumn . editMessageId !== "0"
2020-10-18 22:54:15 +03:00
Text {
width: parent . width - Theme . paddingSmall - removeEditMessageIconButton . width
anchors.verticalCenter: parent . verticalCenter
id: editMessageText
font.pixelSize: Theme . fontSizeSmall
font.bold: true
text: qsTr ( "Edit Message" )
color: Theme . secondaryColor
}
IconButton {
id: removeEditMessageIconButton
icon.source: "image://theme/icon-m-clear"
onClicked: {
newMessageColumn . editMessageId = "0" ;
newMessageTextField . text = "" ;
}
}
2020-09-19 00:43:23 +03:00
}
2020-08-28 18:40:25 +03:00
Row {
id: newMessageRow
width: parent . width
anchors.horizontalCenter: parent . horizontalCenter
2020-09-24 00:41:17 +03:00
TextArea {
id: newMessageTextField
width: parent . width - attachmentIconButton . width - newMessageSendButton . width
height: Math . min ( chatContainer . height / 3 , implicitHeight )
2020-08-28 18:40:25 +03:00
anchors.verticalCenter: parent . verticalCenter
2020-09-24 00:41:17 +03:00
font.pixelSize: Theme . fontSizeSmall
placeholderText: qsTr ( "Your message" )
labelVisible: false
textLeftMargin: 0
textTopMargin: 0
EnterKey.onClicked: {
2020-10-06 04:40:12 +03:00
if ( appSettings . sendByEnter ) {
2020-09-27 14:49:06 +03:00
sendMessage ( ) ;
2020-09-24 00:41:17 +03:00
newMessageTextField . text = "" ;
newMessageTextField . focus = false ;
}
}
2020-10-06 04:40:12 +03:00
EnterKey.enabled: ! 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
2020-09-24 00:41:17 +03:00
onTextChanged: {
2020-09-29 22:00:23 +03:00
controlSendButton ( ) ;
2020-10-18 19:57:01 +03:00
textReplacementTimer . restart ( ) ;
2020-08-28 18:40:25 +03:00
}
}
2020-09-24 00:41:17 +03:00
IconButton {
id: attachmentIconButton
2020-09-27 14:49:06 +03:00
icon.source: attachmentOptionsRow . visible ? "image://theme/icon-m-attach?" + Theme.highlightColor : "image://theme/icon-m-attach?" + Theme . primaryColor
2020-09-24 23:27:17 +03:00
anchors.bottom: parent . bottom
anchors.bottomMargin: Theme . paddingSmall
2020-10-16 00:43:55 +03:00
enabled: ! attachmentPreviewRow . visible
2020-09-24 00:41:17 +03:00
onClicked: {
2020-09-27 14:49:06 +03:00
if ( attachmentOptionsRow . visible ) {
attachmentOptionsRow . visible = false ;
2020-10-16 00:43:55 +03:00
stickerPickerLoader . active = false ;
2020-09-27 14:49:06 +03:00
} else {
attachmentOptionsRow . visible = true ;
}
2020-09-24 00:41:17 +03:00
}
}
IconButton {
id: newMessageSendButton
icon.source: "image://theme/icon-m-chat"
2020-09-24 23:27:17 +03:00
anchors.bottom: parent . bottom
anchors.bottomMargin: Theme . paddingSmall
2020-09-24 00:41:17 +03:00
enabled: false
onClicked: {
2020-09-27 14:49:06 +03:00
sendMessage ( ) ;
2020-09-24 00:41:17 +03:00
newMessageTextField . text = "" ;
newMessageTextField . focus = false ;
2020-08-21 15:26:56 +03:00
}
}
}
}
}
}
}