2020-08-21 15:26:56 +03:00
/ *
Copyright ( C ) 2020 Sebastian J . Wolf
This file is part of Fernschreiber .
Fernschreiber is free software: you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
Fernschreiber is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
You should have received a copy of the GNU General Public License
along with Fernschreiber . If not , see < http: //www.gnu.org/licenses/>.
* /
import QtQuick 2.5
import QtGraphicalEffects 1.0
import QtMultimedia 5.0
import Sailfish . Silica 1.0
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
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-08-21 15:26:56 +03:00
property variant 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 ;
property variant chatPartnerInformation ;
property variant chatGroupInformation ;
property int chatOnlineMemberCount: 0 ;
function getShortenedCount ( count ) {
if ( count >= 1000000 ) {
return qsTr ( "%1M" ) . arg ( ( count / 1000000 ) . toLocaleString ( Qt . locale ( ) , 'f' , 0 ) ) ;
} else if ( count >= 1000 ) {
return qsTr ( "%1K" ) . arg ( ( count / 1000 ) . toLocaleString ( Qt . locale ( ) , 'f' , 0 ) ) ;
} else {
return count ;
}
}
function updateChatPartnerStatusText ( ) {
if ( chatPartnerInformation . status [ '@type' ] === "userStatusEmpty" ) {
chatStatusText . text = qsTr ( "was never online" ) ;
}
if ( chatPartnerInformation . status [ '@type' ] === "userStatusLastMonth" ) {
chatStatusText . text = qsTr ( "offline, last online: last month" ) ;
}
if ( chatPartnerInformation . status [ '@type' ] === "userStatusLastWeek" ) {
chatStatusText . text = qsTr ( "offline, last online: last week" ) ;
}
if ( chatPartnerInformation . status [ '@type' ] === "userStatusOffline" ) {
chatStatusText . text = qsTr ( "offline, last online: %1" ) . arg ( Functions . getDateTimeElapsed ( chatPartnerInformation . status . was_online ) ) ;
}
if ( chatPartnerInformation . status [ '@type' ] === "userStatusOnline" ) {
chatStatusText . text = qsTr ( "online" ) ;
}
if ( chatPartnerInformation . status [ '@type' ] === "userStatusRecently" ) {
chatStatusText . text = qsTr ( "offline, was recently online" ) ;
}
}
function updateGroupStatusText ( ) {
if ( chatOnlineMemberCount > 0 ) {
chatStatusText . text = qsTr ( "%1 members, %2 online" ) . arg ( getShortenedCount ( chatGroupInformation . member_count ) ) . arg ( getShortenedCount ( chatOnlineMemberCount ) ) ;
} else {
if ( isChannel ) {
chatStatusText . text = qsTr ( "%1 subscribers" ) . arg ( getShortenedCount ( chatGroupInformation . member_count ) ) ;
} else {
chatStatusText . text = qsTr ( "%1 members" ) . arg ( getShortenedCount ( chatGroupInformation . member_count ) ) ;
}
}
}
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-08-21 15:26:56 +03:00
}
2020-08-31 22:51:52 +03:00
function getMessageStatusText ( message , listItemIndex , lastReadSentIndex ) {
var messageStatusSuffix = "" ;
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 ) ;
}
}
}
return Functions . getDateTimeElapsed ( message . date ) + messageStatusSuffix ;
}
2020-08-21 15:26:56 +03:00
Component.onCompleted: {
initializePage ( ) ;
}
2020-08-21 15:47:08 +03:00
onStatusChanged: {
2020-08-25 00:02:08 +03:00
if ( status === PageStatus . Activating ) {
tdLibWrapper . openChat ( chatInformation . id ) ;
}
2020-08-29 19:04:23 +03:00
if ( status === PageStatus . Active ) {
2020-08-30 00:59:29 +03:00
if ( ! chatPage . isInitialized ) {
chatModel . initialize ( chatInformation ) ;
chatPage . isInitialized = true ;
}
2020-08-29 19:04:23 +03:00
}
2020-08-21 15:47:08 +03:00
if ( status === PageStatus . Deactivating ) {
tdLibWrapper . closeChat ( chatInformation . id ) ;
}
}
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-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 ) ;
chatView . lastReadSentIndex = lastReadSentIndex ;
2020-09-20 15:26:09 +03:00
if ( modelIndex === ( chatView . count - 1 ) ) {
chatView . positionViewAtEnd ( ) ;
} else {
chatView . positionViewAtIndex ( modelIndex , ListView . Beginning ) ;
}
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..." ) ;
tdLibWrapper . viewMessage ( chatInformation . id , chatModel . getMessage ( chatView . count - 1 ) . id ) ;
}
2020-08-22 22:43:20 +03:00
}
2020-08-23 00:49:02 +03:00
onNewMessageReceived: {
2020-09-22 21:32:35 +03:00
if ( message . sender_user_id === chatPage . myUserId ) {
console . log ( "[ChatPage] Own message received, scrolling down to see it..." ) ;
chatView . scrollToBottom ( ) ;
}
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 . currentIndex = modelIndex ;
chatView . lastReadSentIndex = lastReadSentIndex ;
2020-09-22 22:05:24 +03:00
chatViewCooldownTimer . start ( ) ;
2020-09-01 23:37:48 +03:00
}
2020-08-22 22:43:20 +03:00
}
2020-08-21 19:03:51 +03:00
Timer {
id: chatContactTimeUpdater
interval: 60000
running: true
repeat: true
onTriggered: {
updateChatPartnerStatusText ( ) ;
}
}
2020-08-21 15:26:56 +03:00
SilicaFlickable {
id: chatContainer
contentHeight: parent . height
contentWidth: parent . width
anchors.fill: parent
2020-09-16 21:43:36 +03:00
PullDownMenu {
2020-09-19 00:43:23 +03:00
visible: chatInformation . id !== chatPage . myUserId
2020-09-16 21:43:36 +03:00
MenuItem {
id: muteChatMenuItem
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" )
}
}
Connections {
target: chatModel
onNotificationSettingsUpdated: {
chatInformation = chatModel . getChatInformation ( ) ;
muteChatMenuItem . text = chatInformation . notification_settings . mute_for > 0 ? qsTr ( "Unmute Chat" ) : qsTr ( "Mute Chat" ) ;
}
}
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
color: Theme . secondaryColor
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-08-29 22:39:57 +03:00
Timer {
id: chatViewLoadingTimer
2020-08-30 00:06:14 +03:00
interval: 100
2020-08-29 22:39:57 +03:00
repeat: false
running: false
onTriggered: {
chatPage . loading = false ;
}
}
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 ;
}
}
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-08-23 00:05:45 +03:00
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-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-08-29 19:04:23 +03:00
tdLibWrapper . viewMessage ( chatInformation . id , chatView . itemAt ( chatView . contentX , ( chatView . contentY + chatView . height - Theme . horizontalPageMargin ) ) . myMessage . id ) ;
}
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 ( ) ;
}
}
model: chatModel
delegate: ListItem {
2020-08-21 15:26:56 +03:00
2020-08-29 19:04:23 +03:00
id: messageListItem
contentHeight: messageBackground . height + Theme . paddingMedium
contentWidth: parent . width
2020-08-21 15:26:56 +03:00
2020-08-29 19:04:23 +03:00
property variant myMessage: display
property variant userInformation: tdLibWrapper . getUserInformation ( display . sender_user_id )
2020-08-23 00:05:45 +03:00
2020-08-29 19:04:23 +03:00
menu: ContextMenu {
MenuItem {
onClicked: {
newMessageInReplyToRow . inReplyToMessage = display ;
newMessageTextField . focus = true ;
}
text: qsTr ( "Reply to Message" )
2020-08-28 18:40:25 +03:00
}
2020-09-19 00:43:23 +03:00
MenuItem {
onClicked: {
newMessageColumn . editMessageId = display . id ;
newMessageTextField . text = Functions . getMessageText ( display , false ) ;
2020-09-19 19:49:11 +03:00
newMessageTextField . focus = true ;
2020-09-19 00:43:23 +03:00
}
text: qsTr ( "Edit Message" )
visible: display . can_be_edited
}
2020-09-20 01:13:42 +03:00
MenuItem {
onClicked: {
deleteMessageRemorseItem . execute ( messageListItem , qsTr ( "Deleting message" ) , function ( ) { tdLibWrapper . deleteMessages ( chatInformation . id , [ display . id ] ) ; } ) ;
}
text: qsTr ( "Delete Message" )
visible: display . can_be_deleted_for_all_users
}
2020-08-28 18:40:25 +03:00
}
2020-09-15 23:47:39 +03:00
Connections {
target: chatModel
onUnreadCountUpdated: {
messageBackground . color = index > ( chatView . count - unreadCount - 1 ) ? Theme.secondaryHighlightColor : Theme . secondaryColor ;
messageBackground . opacity = index > ( chatView . count - unreadCount - 1 ) ? 0.5 : 0.2 ;
}
2020-09-19 19:49:11 +03:00
onNewMessageReceived: {
messageBackground . color = index > ( chatView . count - chatInformation . unreadCount - 1 ) ? Theme.secondaryHighlightColor : Theme . secondaryColor ;
messageBackground . opacity = index > ( chatView . count - chatInformation . unreadCount - 1 ) ? 0.5 : 0.2 ;
}
2020-09-15 23:47:39 +03:00
}
2020-09-22 23:46:48 +03:00
Component.onCompleted: {
delegateComponentLoadingTimer . start ( ) ;
}
Timer {
id: delegateComponentLoadingTimer
interval: 500
repeat: false
running: false
onTriggered: {
webPagePreviewLoader . active = ( typeof display . content . web_page !== "undefined" ) ;
imagePreviewLoader . active = ( display . content [ '@type' ] === "messagePhoto" ) ;
stickerPreviewLoader . active = ( display . content [ '@type' ] === "messageSticker" ) ;
videoPreviewLoader . active = ( ( display . content [ '@type' ] === "messageVideo" ) || ( display . content [ '@type' ] === "messageAnimation" ) ) ;
audioPreviewLoader . active = ( ( display . content [ '@type' ] === "messageVoiceNote" ) || ( display . content [ '@type' ] === "messageAudio" ) ) ;
documentPreviewLoader . active = ( display . content [ '@type' ] === "messageDocument" ) ;
}
}
2020-09-20 01:13:42 +03:00
RemorseItem {
id: deleteMessageRemorseItem
}
2020-08-29 19:04:23 +03:00
Row {
id: messageTextRow
spacing: Theme . paddingSmall
width: parent . width - ( 2 * Theme . horizontalPageMargin )
anchors.horizontalCenter: parent . horizontalCenter
2020-09-15 22:27:16 +03:00
anchors.verticalCenter: parent . verticalCenter
2020-08-29 19:04:23 +03:00
2020-09-22 00:07:30 +03:00
Component {
id: profileThumbnailComponent
ProfileThumbnail {
id: messagePictureThumbnail
photoData: ( typeof messageListItem . userInformation . profile_photo !== "undefined" ) ? messageListItem.userInformation.profile_photo.small : ""
replacementStringHint: userText . text
width: visible ? Theme.itemSizeSmall : 0
height: visible ? Theme.itemSizeSmall : 0
visible: ( chatPage . isBasicGroup || chatPage . isSuperGroup ) && ! chatPage . isChannel
}
}
Loader {
id: profileThumbnailLoader
2020-09-22 23:46:48 +03:00
active: ( ( chatPage . isBasicGroup || chatPage . isSuperGroup ) && ! chatPage . isChannel )
2020-09-22 00:07:30 +03:00
asynchronous: true
width: active ? Theme.itemSizeSmall : 0
height: active ? Theme.itemSizeSmall : 0
2020-08-29 19:04:23 +03:00
anchors.bottom: parent . bottom
anchors.bottomMargin: Theme . paddingSmall
2020-09-22 00:07:30 +03:00
sourceComponent: profileThumbnailComponent
2020-08-29 19:04:23 +03:00
}
2020-08-23 20:17:30 +03:00
2020-08-29 19:04:23 +03:00
Item {
id: messageTextItem
2020-08-22 22:43:20 +03:00
2020-09-22 00:07:30 +03:00
width: parent . width - profileThumbnailLoader . width - Theme . paddingSmall
2020-08-29 19:04:23 +03:00
height: messageBackground . height
2020-08-23 20:17:30 +03:00
2020-08-29 19:04:23 +03:00
Rectangle {
id: messageBackground
anchors {
left: parent . left
leftMargin: ( chatPage . myUserId === display . sender_user_id ) ? 2 * Theme.horizontalPageMargin : 0
right: parent . right
rightMargin: ( chatPage . myUserId === display . sender_user_id ) ? 0 : 2 * Theme . horizontalPageMargin
verticalCenter: parent . verticalCenter
}
height: messageTextColumn . height + ( 2 * Theme . paddingMedium )
2020-08-23 20:17:30 +03:00
2020-09-15 23:47:39 +03:00
color: index > ( chatView . count - chatInformation . unread_count - 1 ) ? Theme.secondaryHighlightColor : Theme . secondaryColor
2020-08-29 19:04:23 +03:00
radius: parent . width / 50
2020-09-15 23:47:39 +03:00
opacity: index > ( chatView . count - chatInformation . unread_count - 1 ) ? 0.5 : 0.2
2020-08-23 20:17:30 +03:00
}
2020-08-22 22:43:20 +03:00
2020-08-29 19:04:23 +03:00
Column {
id: messageTextColumn
2020-08-23 20:17:30 +03:00
2020-08-29 19:04:23 +03:00
spacing: Theme . paddingSmall
2020-08-23 20:17:30 +03:00
2020-08-29 19:04:23 +03:00
width: messageBackground . width - Theme . horizontalPageMargin
anchors.centerIn: messageBackground
2020-08-23 20:17:30 +03:00
2020-09-22 23:46:48 +03:00
Behavior on height {
PropertyAnimation { easing.type: Easing . OutBack ; duration: 200 }
2020-08-29 12:22:18 +03:00
}
2020-08-29 19:04:23 +03:00
Connections {
target: tdLibWrapper
onReceivedMessage: {
if ( messageId === display . reply_to_message_id . toString ( ) ) {
messageInReplyToRow . inReplyToMessage = message ;
messageInReplyToRow . visible = true ;
}
2020-08-29 12:22:18 +03:00
}
}
2020-08-29 19:04:23 +03:00
Text {
id: userText
width: parent . width
text: display . sender_user_id !== chatPage . myUserId ? Emoji . emojify ( Functions . getUserName ( messageListItem . userInformation ) , font . pixelSize ) : qsTr ( "You" )
font.pixelSize: Theme . fontSizeExtraSmall
font.weight: Font . ExtraBold
color: ( chatPage . myUserId === display . sender_user_id ) ? Theme.highlightColor : Theme . primaryColor
maximumLineCount: 1
elide: Text . ElideRight
textFormat: Text . StyledText
horizontalAlignment: ( chatPage . myUserId === display . sender_user_id ) ? Text.AlignRight : Text . AlignLeft
visible: ( chatPage . isBasicGroup || chatPage . isSuperGroup ) && ! chatPage . isChannel
}
2020-08-23 20:17:30 +03:00
2020-08-29 19:04:23 +03:00
InReplyToRow {
id: messageInReplyToRow
myUserId: chatPage . myUserId
visible: false
}
2020-08-25 17:42:46 +03:00
2020-08-29 19:04:23 +03:00
Text {
id: messageText
width: parent . width
text: Emoji . emojify ( Functions . getMessageText ( display , false ) , font . pixelSize )
font.pixelSize: Theme . fontSizeSmall
color: ( chatPage . myUserId === display . sender_user_id ) ? Theme.highlightColor : Theme . primaryColor
wrapMode: Text . Wrap
textFormat: Text . StyledText
onLinkActivated: {
Functions . handleLink ( link ) ;
}
horizontalAlignment: ( chatPage . myUserId === display . sender_user_id ) ? Text.AlignRight : Text . AlignLeft
linkColor: Theme . highlightColor
visible: ( text !== "" )
2020-08-23 20:17:30 +03:00
}
2020-08-25 00:02:08 +03:00
2020-09-21 23:10:03 +03:00
Component {
id: webPagePreviewComponent
WebPagePreview {
id: webPagePreview
webPageData: ( typeof display . content . web_page !== "undefined" ) ? display.content.web_page : ""
width: parent . width
visible: typeof display . content . web_page !== "undefined"
}
}
Loader {
id: webPagePreviewLoader
2020-09-22 23:46:48 +03:00
active: false
2020-09-21 23:10:03 +03:00
asynchronous: true
width: parent . width
sourceComponent: webPagePreviewComponent
}
Component {
id: imagePreviewComponent
ImagePreview {
id: messageImagePreview
photoData: ( display . content [ '@type' ] === "messagePhoto" ) ? display.content.photo : ""
width: parent . width
height: parent . width * 2 / 3
visible: display . content [ '@type' ] === "messagePhoto"
}
}
Loader {
id: imagePreviewLoader
2020-09-22 23:46:48 +03:00
active: false
2020-09-21 23:10:03 +03:00
asynchronous: true
2020-08-29 19:04:23 +03:00
width: parent . width
2020-09-21 23:10:03 +03:00
sourceComponent: imagePreviewComponent
2020-08-29 19:04:23 +03:00
}
2020-08-29 16:51:48 +03:00
2020-09-21 23:10:03 +03:00
Component {
id: stickerPreviewComponent
StickerPreview {
id: messageStickerPreview
stickerData: ( display . content [ '@type' ] === "messageSticker" ) ? display.content.sticker : ""
visible: display . content [ '@type' ] === "messageSticker"
anchors.horizontalCenter: parent . horizontalCenter
}
}
Loader {
id: stickerPreviewLoader
2020-09-22 23:46:48 +03:00
active: false
2020-09-21 23:10:03 +03:00
asynchronous: true
2020-08-29 19:04:23 +03:00
width: parent . width
2020-09-21 23:10:03 +03:00
sourceComponent: stickerPreviewComponent
2020-08-29 19:04:23 +03:00
}
2020-08-23 20:17:30 +03:00
2020-09-21 23:10:03 +03:00
Component {
id: videoPreviewComponent
VideoPreview {
id: messageVideoPreview
videoData: ( display . content [ '@type' ] === "messageVideo" ) ? display.content.video : ( ( display . content [ '@type' ] === "messageAnimation" ) ? display.content.animation : "" )
width: parent . width
height: ( display . content [ '@type' ] === "messageVideo" ) ? Functions . getVideoHeight ( width , display . content . video ) : Functions . getVideoHeight ( width , display . content . animation )
visible: ( display . content [ '@type' ] === "messageVideo" || display . content [ '@type' ] === "messageAnimation" )
onScreen: chatPage . status === PageStatus . Active
}
2020-08-29 19:04:23 +03:00
}
2020-08-26 11:46:43 +03:00
2020-09-21 23:10:03 +03:00
Loader {
id: videoPreviewLoader
2020-09-22 23:46:48 +03:00
active: false
2020-09-21 23:10:03 +03:00
asynchronous: true
2020-08-29 19:04:23 +03:00
width: parent . width
2020-09-21 23:10:03 +03:00
sourceComponent: videoPreviewComponent
2020-08-29 19:04:23 +03:00
}
2020-08-28 11:41:18 +03:00
2020-09-21 23:10:03 +03:00
Component {
id: audioPreviewComponent
AudioPreview {
id: messageAudioPreview
audioData: ( display . content [ '@type' ] === "messageVoiceNote" ) ? display.content.voice_note : ( ( display . content [ '@type' ] === "messageAudio" ) ? display.content.audio : "" )
width: parent . width
height: parent . width / 2
visible: ( display . content [ '@type' ] === "messageVoiceNote" || display . content [ '@type' ] === "messageAudio" )
onScreen: chatPage . status === PageStatus . Active
}
}
Loader {
id: audioPreviewLoader
2020-09-22 23:46:48 +03:00
active: false
2020-09-21 23:10:03 +03:00
asynchronous: true
2020-08-29 19:04:23 +03:00
width: parent . width
2020-09-21 23:10:03 +03:00
sourceComponent: audioPreviewComponent
2020-08-29 19:04:23 +03:00
}
2020-08-28 11:41:18 +03:00
2020-09-21 23:10:03 +03:00
Component {
id: documentPreviewComponent
DocumentPreview {
id: messageDocumentPreview
documentData: ( display . content [ '@type' ] === "messageDocument" ) ? display.content.document : ""
visible: display . content [ '@type' ] === "messageDocument"
}
}
Loader {
id: documentPreviewLoader
2020-09-22 23:46:48 +03:00
active: false
2020-09-21 23:10:03 +03:00
asynchronous: true
width: parent . width
sourceComponent: documentPreviewComponent
2020-08-29 19:04:23 +03:00
}
2020-08-28 17:18:33 +03:00
2020-08-29 19:04:23 +03:00
Timer {
id: messageDateUpdater
interval: 60000
running: true
repeat: true
onTriggered: {
2020-08-31 22:51:52 +03:00
messageDateText . text = getMessageStatusText ( display , index , chatView . lastReadSentIndex ) ;
2020-08-30 20:04:16 +03:00
}
}
Connections {
target: chatModel
onLastReadSentMessageUpdated: {
2020-08-31 00:52:22 +03:00
console . log ( "[ChatModel] Messages in this chat were read, new last read: " + lastReadSentIndex + ", updating description for index " + index + ", status: " + ( index <= lastReadSentIndex ) ) ;
2020-08-31 22:51:52 +03:00
messageDateText . text = getMessageStatusText ( display , index , lastReadSentIndex ) ;
2020-08-29 19:04:23 +03:00
}
2020-09-19 21:33:51 +03:00
onMessageUpdated: {
if ( index === modelIndex ) {
console . log ( "[ChatModel] This message was updated, index " + index + ", updating content..." ) ;
messageDateText . text = getMessageStatusText ( display , index , chatView . lastReadSentIndex ) ;
2020-09-20 15:26:09 +03:00
messageText . text = Emoji . emojify ( Functions . getMessageText ( display , false ) , messageText . font . pixelSize ) ;
2020-09-19 21:33:51 +03:00
}
}
2020-08-23 20:17:30 +03:00
}
2020-08-29 19:04:23 +03:00
Text {
width: parent . width
id: messageDateText
font.pixelSize: Theme . fontSizeTiny
color: ( chatPage . myUserId === display . sender_user_id ) ? Theme.secondaryHighlightColor : Theme . secondaryColor
horizontalAlignment: ( chatPage . myUserId === display . sender_user_id ) ? Text.AlignRight : Text . AlignLeft
2020-08-31 22:51:52 +03:00
text: getMessageStatusText ( display , index , chatView . lastReadSentIndex )
2020-08-29 19:04:23 +03:00
}
2020-08-23 20:17:30 +03:00
}
2020-08-22 22:43:20 +03:00
}
}
}
2020-08-29 19:04:23 +03:00
VerticalScrollDecorator { }
}
Column {
width: parent . width
height: loadingLabel . height + loadingBusyIndicator . height + Theme . paddingMedium
spacing: Theme . paddingMedium
anchors.verticalCenter: parent . verticalCenter
opacity: chatPage . loading ? 1 : 0
Behavior on opacity { NumberAnimation { } }
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: {
chatView . positionViewAtIndex ( chatView . count - 1 - chatInformation . unread_count , ListView . Beginning ) ;
}
}
2020-08-29 22:39:57 +03:00
}
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
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
}
}
id: newMessageInReplyToRow
myUserId: chatPage . myUserId
2020-08-28 18:40:25 +03:00
anchors.horizontalCenter: parent . horizontalCenter
2020-08-29 12:22:18 +03:00
visible: false
2020-08-21 15:26:56 +03:00
}
2020-09-19 00:43:23 +03:00
Text {
width: parent . width
id: editMessageText
font.pixelSize: Theme . fontSizeSmall
font.bold: true
text: qsTr ( "Edit Message" )
color: Theme . secondaryColor
visible: newMessageColumn . editMessageId !== "0"
}
2020-08-28 18:40:25 +03:00
Row {
id: newMessageRow
width: parent . width
2020-09-19 00:43:23 +03:00
height: sendMessageColumn . height + Theme . paddingMedium
2020-08-28 18:40:25 +03:00
anchors.horizontalCenter: parent . horizontalCenter
Column {
id: sendMessageColumn
2020-09-13 23:21:27 +03:00
width: parent . width - newMessageSendButton . width
2020-08-28 18:40:25 +03:00
anchors.verticalCenter: parent . verticalCenter
2020-09-13 23:21:27 +03:00
2020-08-28 18:40:25 +03:00
TextArea {
id: newMessageTextField
width: parent . width
font.pixelSize: Theme . fontSizeSmall
placeholderText: qsTr ( "Your message" )
labelVisible: false
2020-09-13 23:21:27 +03:00
textLeftMargin: 0
2020-09-19 00:43:23 +03:00
textTopMargin: 0
2020-08-28 18:40:25 +03:00
onFocusChanged: {
if ( ! focus ) {
2020-08-29 17:58:48 +03:00
newMessageInReplyToRow . inReplyToMessage = null ;
2020-09-19 00:43:23 +03:00
if ( newMessageColumn . editMessageId !== "0" ) {
newMessageColumn . editMessageId = "0" ;
newMessageTextField . text = "" ;
}
2020-08-28 18:40:25 +03:00
}
}
2020-09-16 23:04:02 +03:00
EnterKey.onClicked: {
if ( tdLibWrapper . getSendByEnter ( ) ) {
2020-09-19 21:33:51 +03:00
if ( newMessageColumn . editMessageId !== "0" ) {
tdLibWrapper . editMessageText ( chatInformation . id , newMessageColumn . editMessageId , newMessageTextField . text ) ;
} else {
tdLibWrapper . sendTextMessage ( chatInformation . id , newMessageTextField . text , newMessageColumn . replyToMessageId ) ;
}
2020-09-16 23:04:02 +03:00
newMessageTextField . text = "" ;
newMessageTextField . focus = false ;
}
}
Component.onCompleted: {
if ( tdLibWrapper . getSendByEnter ( ) ) {
EnterKey . iconSource = "image://theme/icon-m-chat" ;
EnterKey . enabled = false ;
}
}
onTextChanged: {
if ( text . length === 0 ) {
newMessageSendButton . enabled = false ;
if ( tdLibWrapper . getSendByEnter ( ) ) {
EnterKey . enabled = false ;
}
} else {
newMessageSendButton . enabled = true ;
if ( tdLibWrapper . getSendByEnter ( ) ) {
EnterKey . enabled = true ;
}
}
}
2020-08-28 18:40:25 +03:00
}
}
Column {
anchors.bottom: parent . bottom
2020-09-19 00:43:23 +03:00
anchors.bottomMargin: Theme . paddingSmall
2020-09-13 23:21:27 +03:00
2020-08-28 18:40:25 +03:00
IconButton {
id: newMessageSendButton
icon.source: "image://theme/icon-m-chat"
anchors.horizontalCenter: parent . horizontalCenter
2020-09-16 23:04:02 +03:00
enabled: false
2020-08-28 18:40:25 +03:00
onClicked: {
2020-09-19 21:33:51 +03:00
if ( newMessageColumn . editMessageId !== "0" ) {
tdLibWrapper . editMessageText ( chatInformation . id , newMessageColumn . editMessageId , newMessageTextField . text ) ;
} else {
tdLibWrapper . sendTextMessage ( chatInformation . id , newMessageTextField . text , newMessageColumn . replyToMessageId ) ;
}
2020-08-28 18:40:25 +03:00
newMessageTextField . text = "" ;
newMessageTextField . focus = false ;
}
2020-08-21 15:26:56 +03:00
}
}
}
2020-08-28 18:40:25 +03:00
2020-08-21 15:26:56 +03:00
}
2020-08-28 18:40:25 +03:00
2020-08-21 15:26:56 +03:00
}
}
}