2020-08-22 18:30:02 +03:00
# include "chatmodel.h"
2020-08-22 22:43:20 +03:00
# include <QListIterator>
2020-08-28 11:41:18 +03:00
# include <QByteArray>
# include <QBitArray>
2020-08-22 22:43:20 +03:00
2020-08-22 18:30:02 +03:00
ChatModel : : ChatModel ( TDLibWrapper * tdLibWrapper )
{
this - > tdLibWrapper = tdLibWrapper ;
2020-08-22 22:43:20 +03:00
this - > inReload = false ;
2020-08-26 23:52:06 +03:00
this - > inIncrementalUpdate = false ;
2020-08-22 22:43:20 +03:00
connect ( this - > tdLibWrapper , SIGNAL ( messagesReceived ( QVariantList ) ) , this , SLOT ( handleMessagesReceived ( QVariantList ) ) ) ;
2020-08-23 00:49:02 +03:00
connect ( this - > tdLibWrapper , SIGNAL ( newMessageReceived ( QString , QVariantMap ) ) , this , SLOT ( handleNewMessageReceived ( QString , QVariantMap ) ) ) ;
2020-08-31 22:51:52 +03:00
connect ( this - > tdLibWrapper , SIGNAL ( chatReadInboxUpdated ( QString , QString , int ) ) , this , SLOT ( handleChatReadInboxUpdated ( QString , QString , int ) ) ) ;
2020-08-30 20:04:16 +03:00
connect ( this - > tdLibWrapper , SIGNAL ( chatReadOutboxUpdated ( QString , QString ) ) , this , SLOT ( handleChatReadOutboxUpdated ( QString , QString ) ) ) ;
2020-08-31 00:52:22 +03:00
connect ( this - > tdLibWrapper , SIGNAL ( messageSendSucceeded ( QString , QString , QVariantMap ) ) , this , SLOT ( handleMessageSendSucceeded ( QString , QString , QVariantMap ) ) ) ;
2020-08-22 18:30:02 +03:00
}
ChatModel : : ~ ChatModel ( )
{
}
int ChatModel : : rowCount ( const QModelIndex & ) const
{
return messages . size ( ) ;
}
QVariant ChatModel : : data ( const QModelIndex & index , int role ) const
{
if ( index . isValid ( ) & & role = = Qt : : DisplayRole ) {
return QVariant ( messages . value ( index . row ( ) ) ) ;
}
return QVariant ( ) ;
}
bool ChatModel : : insertRows ( int row , int count , const QModelIndex & parent )
{
qDebug ( ) < < " [ChatModel] Inserting at " < < row < < " , row count: " < < count ;
beginInsertRows ( parent , row , row + count - 1 ) ;
2020-08-22 22:43:20 +03:00
for ( int i = 0 ; i < count ; i + + ) {
this - > messages . insert ( row + i , this - > messagesToBeAdded . at ( i ) ) ;
}
2020-08-30 20:04:16 +03:00
this - > calculateMessageIndexMap ( ) ;
2020-08-22 18:30:02 +03:00
endInsertRows ( ) ;
return true ;
}
2020-08-22 22:43:20 +03:00
2020-08-29 22:39:57 +03:00
void ChatModel : : initialize ( const QVariantMap & chatInformation )
2020-08-22 22:43:20 +03:00
{
2020-08-30 20:04:16 +03:00
qDebug ( ) < < " [ChatModel] Initializing chat model... " ;
2020-08-29 22:39:57 +03:00
this - > chatInformation = chatInformation ;
2020-08-22 22:43:20 +03:00
this - > messages . clear ( ) ;
this - > messageIndexMap . clear ( ) ;
this - > messagesToBeAdded . clear ( ) ;
2020-08-29 22:39:57 +03:00
this - > chatId = chatInformation . value ( " id " ) . toString ( ) ;
tdLibWrapper - > getChatHistory ( this - > chatId ) ;
2020-08-22 22:43:20 +03:00
}
2020-08-26 23:52:06 +03:00
void ChatModel : : triggerLoadMoreHistory ( )
{
if ( ! this - > inIncrementalUpdate ) {
2020-08-30 16:04:15 +03:00
qDebug ( ) < < " [ChatModel] Trigger loading older history... " ;
2020-08-26 23:52:06 +03:00
this - > inIncrementalUpdate = true ;
this - > tdLibWrapper - > getChatHistory ( this - > chatId , this - > messages . first ( ) . toMap ( ) . value ( " id " ) . toLongLong ( ) ) ;
}
}
2020-08-22 22:43:20 +03:00
bool compareMessages ( const QVariant & message1 , const QVariant & message2 )
{
QVariantMap messageMap1 = message1 . toMap ( ) ;
QVariantMap messageMap2 = message2 . toMap ( ) ;
if ( messageMap1 . value ( " id " ) . toLongLong ( ) < messageMap2 . value ( " id " ) . toLongLong ( ) ) {
return true ;
} else {
return false ;
}
}
void ChatModel : : handleMessagesReceived ( const QVariantList & messages )
{
2020-08-23 18:24:05 +03:00
qDebug ( ) < < " [ChatModel] Receiving new messages :) " < < messages . size ( ) ;
if ( messages . size ( ) = = 0 ) {
2020-08-29 19:28:57 +03:00
qDebug ( ) < < " [ChatModel] No additional messages loaded, notifying chat UI... " ;
2020-08-23 00:49:02 +03:00
this - > inReload = false ;
2020-08-30 20:04:16 +03:00
int listInboxPosition = this - > calculateLastKnownMessageId ( ) ;
int listOutboxPosition = this - > calculateLastReadSentMessageId ( ) ;
2020-08-26 23:52:06 +03:00
if ( this - > inIncrementalUpdate ) {
this - > inIncrementalUpdate = false ;
2020-08-30 20:04:16 +03:00
emit messagesIncrementalUpdate ( listInboxPosition , listOutboxPosition ) ;
2020-08-26 23:52:06 +03:00
} else {
2020-08-30 20:04:16 +03:00
emit messagesReceived ( listInboxPosition , listOutboxPosition ) ;
2020-08-26 23:52:06 +03:00
}
2020-08-29 19:28:57 +03:00
} else {
this - > messagesMutex . lock ( ) ;
this - > messagesToBeAdded . clear ( ) ;
QListIterator < QVariant > messagesIterator ( messages ) ;
while ( messagesIterator . hasNext ( ) ) {
QVariantMap currentMessage = messagesIterator . next ( ) . toMap ( ) ;
if ( currentMessage . value ( " chat_id " ) . toString ( ) = = this - > chatId ) {
this - > messagesToBeAdded . append ( currentMessage ) ;
}
}
std : : sort ( this - > messagesToBeAdded . begin ( ) , this - > messagesToBeAdded . end ( ) , compareMessages ) ;
this - > insertMessages ( ) ;
this - > messagesMutex . unlock ( ) ;
// First call only returns a few messages, we need to get a little more than that...
if ( this - > messagesToBeAdded . size ( ) < 10 & & ! this - > inReload ) {
qDebug ( ) < < " [ChatModel] Only a few messages received in first call, loading more... " ;
this - > inReload = true ;
this - > tdLibWrapper - > getChatHistory ( this - > chatId , this - > messagesToBeAdded . first ( ) . toMap ( ) . value ( " id " ) . toLongLong ( ) ) ;
} else {
qDebug ( ) < < " [ChatModel] Messages loaded, notifying chat UI... " ;
this - > inReload = false ;
2020-08-30 20:04:16 +03:00
int listInboxPosition = this - > calculateLastKnownMessageId ( ) ;
int listOutboxPosition = this - > calculateLastReadSentMessageId ( ) ;
2020-08-29 19:28:57 +03:00
if ( this - > inIncrementalUpdate ) {
this - > inIncrementalUpdate = false ;
2020-08-30 20:04:16 +03:00
emit messagesIncrementalUpdate ( listInboxPosition , listOutboxPosition ) ;
2020-08-29 19:28:57 +03:00
} else {
2020-08-30 20:04:16 +03:00
emit messagesReceived ( listInboxPosition , listOutboxPosition ) ;
2020-08-29 19:28:57 +03:00
}
}
2020-08-23 00:49:02 +03:00
}
2020-08-29 19:28:57 +03:00
2020-08-23 00:49:02 +03:00
}
void ChatModel : : handleNewMessageReceived ( const QString & chatId , const QVariantMap & message )
{
if ( chatId = = this - > chatId ) {
qDebug ( ) < < " [ChatModel] New message received for this chat " ;
this - > messagesMutex . lock ( ) ;
this - > messagesToBeAdded . clear ( ) ;
this - > messagesToBeAdded . append ( message ) ;
this - > insertMessages ( ) ;
this - > messagesMutex . unlock ( ) ;
emit newMessageReceived ( ) ;
}
}
2020-08-31 22:51:52 +03:00
void ChatModel : : handleChatReadInboxUpdated ( const QString & chatId , const QString & lastReadInboxMessageId , const int & unreadCount )
2020-08-29 22:39:57 +03:00
{
if ( chatId = = this - > chatId ) {
2020-08-31 22:51:52 +03:00
qDebug ( ) < < " [ChatModel] Updating chat unread count, unread messages " < < unreadCount < < " , last read message ID: " < < lastReadInboxMessageId ;
2020-08-29 22:39:57 +03:00
this - > chatInformation . insert ( " unread_count " , unreadCount ) ;
2020-08-31 22:51:52 +03:00
this - > chatInformation . insert ( " last_read_inbox_message_id " , lastReadInboxMessageId ) ;
emit unreadCountUpdated ( unreadCount , lastReadInboxMessageId ) ;
2020-08-29 22:39:57 +03:00
}
}
2020-08-30 20:04:16 +03:00
void ChatModel : : handleChatReadOutboxUpdated ( const QString & chatId , const QString & lastReadOutboxMessageId )
{
if ( chatId = = this - > chatId ) {
this - > chatInformation . insert ( " last_read_outbox_message_id " , lastReadOutboxMessageId ) ;
int sentIndex = calculateLastReadSentMessageId ( ) ;
qDebug ( ) < < " [ChatModel] Updating sent message ID, new index " < < sentIndex ;
emit lastReadSentMessageUpdated ( sentIndex ) ;
}
}
2020-08-31 00:52:22 +03:00
void ChatModel : : handleMessageSendSucceeded ( const QString & messageId , const QString & oldMessageId , const QVariantMap & message )
{
qDebug ( ) < < " [ChatModel] Message send succeeded, new message ID " < < messageId < < " old message ID " < < oldMessageId < < " , chat ID " < < message . value ( " chat_id " ) . toString ( ) ;
qDebug ( ) < < " [ChatModel] index map: " < < this - > messageIndexMap . contains ( oldMessageId ) < < " , index count: " < < this - > messageIndexMap . size ( ) < < " , message count: " < < this - > messages . size ( ) ;
if ( this - > messageIndexMap . contains ( oldMessageId ) ) {
this - > messagesMutex . lock ( ) ;
qDebug ( ) < < " [ChatModel] Message was successfully sent " < < oldMessageId ;
int messageIndex = this - > messageIndexMap . value ( oldMessageId ) . toInt ( ) ;
this - > messages . replace ( messageIndex , message ) ;
this - > calculateMessageIndexMap ( ) ;
qDebug ( ) < < " [ChatModel] Message was replaced at index " < < messageIndex ;
this - > messagesMutex . unlock ( ) ;
emit lastReadSentMessageUpdated ( calculateLastReadSentMessageId ( ) ) ;
emit dataChanged ( index ( messageIndex ) , index ( messageIndex ) ) ;
}
}
2020-08-23 00:49:02 +03:00
void ChatModel : : insertMessages ( )
{
2020-08-22 22:43:20 +03:00
if ( this - > messages . isEmpty ( ) ) {
beginResetModel ( ) ;
this - > messages . append ( this - > messagesToBeAdded ) ;
2020-08-30 20:04:16 +03:00
this - > calculateMessageIndexMap ( ) ;
2020-08-22 22:43:20 +03:00
endResetModel ( ) ;
} else {
// There is only an append or a prepend, tertium non datur! (probably ;))
if ( this - > messages . last ( ) . toMap ( ) . value ( " id " ) . toLongLong ( ) < this - > messagesToBeAdded . first ( ) . toMap ( ) . value ( " id " ) . toLongLong ( ) ) {
// Append
this - > insertRows ( rowCount ( QModelIndex ( ) ) , this - > messagesToBeAdded . size ( ) ) ;
} else {
// Prepend
this - > insertRows ( 0 , this - > messagesToBeAdded . size ( ) ) ;
}
}
}
2020-08-28 11:41:18 +03:00
QVariantMap ChatModel : : enhanceMessage ( const QVariantMap & message )
{
QVariantMap enhancedMessage = message ;
if ( enhancedMessage . value ( " content " ) . toMap ( ) . value ( " @type " ) . toString ( ) = = " messageVoiceNote " ) {
QVariantMap contentMap = enhancedMessage . value ( " content " ) . toMap ( ) ;
QVariantMap voiceNoteMap = contentMap . value ( " voice_note " ) . toMap ( ) ;
QByteArray waveBytes = QByteArray : : fromBase64 ( voiceNoteMap . value ( " waveform " ) . toByteArray ( ) ) ;
QBitArray waveBits ( waveBytes . count ( ) * 8 ) ;
for ( int i = 0 ; i < waveBytes . count ( ) ; i + + ) {
for ( int b = 0 ; b < 8 ; b + + ) {
waveBits . setBit ( i * 8 + b , waveBytes . at ( i ) & ( 1 < < ( 7 - b ) ) ) ;
}
}
int waveSize = 10 ;
int waveformSets = waveBits . size ( ) / waveSize ;
QVariantList decodedWaveform ;
for ( int i = 0 ; i < waveformSets ; i + + ) {
int waveformHeight = 0 ;
for ( int j = 0 ; j < waveSize ; j + + ) {
waveformHeight = waveformHeight + ( waveBits . at ( i * waveSize + j ) * ( 2 ^ ( j ) ) ) ;
}
decodedWaveform . append ( waveformHeight ) ;
}
voiceNoteMap . insert ( " decoded_voice_note " , decodedWaveform ) ;
contentMap . insert ( " voice_note " , voiceNoteMap ) ;
enhancedMessage . insert ( " content " , contentMap ) ;
}
return enhancedMessage ;
}
2020-08-30 20:04:16 +03:00
int ChatModel : : calculateLastKnownMessageId ( )
{
qDebug ( ) < < " [ChatModel] calculateLastKnownMessageId " ;
QString lastKnownMessageId = this - > chatInformation . value ( " last_read_inbox_message_id " ) . toString ( ) ;
qDebug ( ) < < " [ChatModel] lastKnownMessageId " < < lastKnownMessageId ;
qDebug ( ) < < " [ChatModel] size messageIndexMap " < < this - > messageIndexMap . size ( ) ;
qDebug ( ) < < " [ChatModel] contains ID? " < < this - > messageIndexMap . contains ( lastKnownMessageId ) ;
2020-09-01 00:50:39 +03:00
// TODO: List sometimes crashes if index is set to the last item. Trying with the second last for now.
int listInboxPosition = this - > messageIndexMap . value ( lastKnownMessageId , this - > messages . size ( ) - 2 ) . toInt ( ) ;
if ( listInboxPosition > this - > messages . size ( ) - 2 ) {
listInboxPosition = this - > messages . size ( ) - 2 ;
}
2020-08-30 20:04:16 +03:00
qDebug ( ) < < " [ChatModel] Last known message is at position " < < listInboxPosition ;
return listInboxPosition ;
}
int ChatModel : : calculateLastReadSentMessageId ( )
{
qDebug ( ) < < " [ChatModel] calculateLastReadSentMessageId " ;
QString lastReadSentMessageId = this - > chatInformation . value ( " last_read_outbox_message_id " ) . toString ( ) ;
qDebug ( ) < < " [ChatModel] lastReadSentMessageId " < < lastReadSentMessageId ;
qDebug ( ) < < " [ChatModel] size messageIndexMap " < < this - > messageIndexMap . size ( ) ;
qDebug ( ) < < " [ChatModel] contains ID? " < < this - > messageIndexMap . contains ( lastReadSentMessageId ) ;
int listOutboxPosition = this - > messageIndexMap . value ( lastReadSentMessageId , this - > messages . size ( ) - 1 ) . toInt ( ) ;
qDebug ( ) < < " [ChatModel] Last read sent message is at position " < < listOutboxPosition ;
return listOutboxPosition ;
}
void ChatModel : : calculateMessageIndexMap ( )
{
qDebug ( ) < < " [ChatModel] calculateMessageIndexMap " ;
this - > messageIndexMap . clear ( ) ;
for ( int i = 0 ; i < this - > messages . size ( ) ; i + + ) {
this - > messageIndexMap . insert ( this - > messages . at ( i ) . toMap ( ) . value ( " id " ) . toString ( ) , i ) ;
}
}