update temp messages also for initial pull of messages

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2024-12-09 18:55:35 +01:00
parent aff7845e83
commit 1efc219e73
No known key found for this signature in database
GPG key ID: C793F8B59F43CE7B
4 changed files with 47 additions and 45 deletions

View file

@ -918,8 +918,7 @@ class ChatActivity :
this.lifecycleScope.launch { this.lifecycleScope.launch {
chatViewModel.getRemoveMessageFlow chatViewModel.getRemoveMessageFlow
.onEach { .onEach {
adapter!!.delete(it) removeMessageById(it.id)
adapter!!.notifyDataSetChanged()
} }
.collect() .collect()
} }
@ -1065,9 +1064,15 @@ class ChatActivity :
} }
private fun removeUnreadMessagesMarker() { private fun removeUnreadMessagesMarker() {
val index = adapter?.getMessagePositionById(UNREAD_MESSAGES_MARKER_ID.toString()) removeMessageById(UNREAD_MESSAGES_MARKER_ID.toString())
}
// do not use adapter.deleteById() as it seems to contain a bug! Use this method instead!
private fun removeMessageById(idToDelete: String) {
val index = adapter?.getMessagePositionById(idToDelete)
if (index != null && index != -1) { if (index != null && index != -1) {
adapter?.items?.removeAt(index) adapter?.items?.removeAt(index)
adapter?.notifyItemRemoved(index)
} }
} }

View file

@ -178,14 +178,19 @@ class OfflineFirstChatRepository @Inject constructor(
if (newestMessageIdFromDb.toInt() != 0) { if (newestMessageIdFromDb.toInt() != 0) {
val limit = getCappedMessagesAmountOfChatBlock(newestMessageIdFromDb) val limit = getCappedMessagesAmountOfChatBlock(newestMessageIdFromDb)
// TODO: somewhere here also handle temp messages. updateUiMessages(chatMessages, showUnreadMessagesMarker)
val list = getMessagesBeforeAndEqual(
showMessagesBeforeAndEqual(
internalConversationId,
newestMessageIdFromDb, newestMessageIdFromDb,
internalConversationId,
limit limit
) )
if (list.isNotEmpty()) {
updateUiMessages(
chatMessages = list,
lookIntoFuture = false,
showUnreadMessagesMarker = false
)
}
// delay is a dirty workaround to make sure messages are added to adapter on initial load before dealing // delay is a dirty workaround to make sure messages are added to adapter on initial load before dealing
// with them (otherwise there is a race condition). // with them (otherwise there is a race condition).
@ -302,7 +307,11 @@ class OfflineFirstChatRepository @Inject constructor(
val weHaveMessagesFromOurself = chatMessages.any { it.actorId == currentUser.userId } val weHaveMessagesFromOurself = chatMessages.any { it.actorId == currentUser.userId }
showUnreadMessagesMarker = showUnreadMessagesMarker && !weHaveMessagesFromOurself showUnreadMessagesMarker = showUnreadMessagesMarker && !weHaveMessagesFromOurself
updateUiMessages(chatMessages, showUnreadMessagesMarker) updateUiMessages(
chatMessages = chatMessages,
lookIntoFuture = true,
showUnreadMessagesMarker = showUnreadMessagesMarker
)
} else { } else {
Log.d(TAG, "resultsFromSync are null or empty") Log.d(TAG, "resultsFromSync are null or empty")
} }
@ -325,25 +334,30 @@ class OfflineFirstChatRepository @Inject constructor(
} }
} }
private suspend fun updateUiMessages(chatMessages : List<ChatMessage>, showUnreadMessagesMarker: Boolean) { private suspend fun updateUiMessages(
chatMessages : List<ChatMessage>,
lookIntoFuture: Boolean,
showUnreadMessagesMarker: Boolean
) {
// remove all temp messages from UI
val oldTempMessages = chatDao.getTempMessagesForConversation(internalConversationId) val oldTempMessages = chatDao.getTempMessagesForConversation(internalConversationId)
.first() .first()
.map(ChatMessageEntity::asModel) .map(ChatMessageEntity::asModel)
oldTempMessages.forEach { _removeMessageFlow.emit(it) } oldTempMessages.forEach { _removeMessageFlow.emit(it) }
val tripleChatMessages = Triple(true, showUnreadMessagesMarker, chatMessages) // add new messages to UI
val tripleChatMessages = Triple(lookIntoFuture, showUnreadMessagesMarker, chatMessages)
_messageFlow.emit(tripleChatMessages) _messageFlow.emit(tripleChatMessages)
// remove temp messages from DB that are now found in the new messages
val chatMessagesReferenceIds = chatMessages.mapTo(HashSet(chatMessages.size)) { it.referenceId } val chatMessagesReferenceIds = chatMessages.mapTo(HashSet(chatMessages.size)) { it.referenceId }
val tempChatMessagesThatCanBeReplaced = oldTempMessages.filter { it.referenceId in chatMessagesReferenceIds } val tempChatMessagesThatCanBeReplaced = oldTempMessages.filter { it.referenceId in chatMessagesReferenceIds }
chatDao.deleteTempChatMessages( chatDao.deleteTempChatMessages(
internalConversationId, internalConversationId,
tempChatMessagesThatCanBeReplaced.map { it.referenceId!! } tempChatMessagesThatCanBeReplaced.map { it.referenceId!! }
) )
// add the remaining temp messages to UI again
val remainingTempMessages = chatDao.getTempMessagesForConversation(internalConversationId) val remainingTempMessages = chatDao.getTempMessagesForConversation(internalConversationId)
.first() .first()
.map(ChatMessageEntity::asModel) .map(ChatMessageEntity::asModel)
@ -706,31 +720,19 @@ class OfflineFirstChatRepository @Inject constructor(
} }
} }
private suspend fun showMessagesBeforeAndEqual(internalConversationId: String, messageId: Long, limit: Int) { suspend fun getMessagesBeforeAndEqual(
suspend fun getMessagesBeforeAndEqual( messageId: Long,
messageId: Long, internalConversationId: String,
internalConversationId: String, messageLimit: Int
messageLimit: Int ): List<ChatMessage> =
): List<ChatMessage> = chatDao.getMessagesForConversationBeforeAndEqual(
chatDao.getMessagesForConversationBeforeAndEqual(
internalConversationId,
messageId,
messageLimit
).map {
it.map(ChatMessageEntity::asModel)
}.first()
val list = getMessagesBeforeAndEqual(
messageId,
internalConversationId, internalConversationId,
limit messageId,
) messageLimit
).map {
it.map(ChatMessageEntity::asModel)
}.first()
if (list.isNotEmpty()) {
val triple = Triple(false, false, list)
_messageFlow.emit(triple)
}
}
private suspend fun showMessagesBefore(internalConversationId: String, messageId: Long, limit: Int) { private suspend fun showMessagesBefore(internalConversationId: String, messageId: Long, limit: Int) {
suspend fun getMessagesBefore( suspend fun getMessagesBefore(
@ -835,9 +837,6 @@ class OfflineFirstChatRepository @Inject constructor(
message.toString(), message.toString(),
referenceId referenceId
) )
// accessing internalConversationId creates UninitializedPropertyException because ChatViewModel and
// MessageInputViewModel use different instances of ChatRepository for now
chatDao.upsertChatMessage(tempChatMessageEntity) chatDao.upsertChatMessage(tempChatMessageEntity)
@ -847,9 +846,6 @@ class OfflineFirstChatRepository @Inject constructor(
val triple = Triple(true, false, listOf(tempChatMessageModel)) val triple = Triple(true, false, listOf(tempChatMessageModel))
_messageFlow.emit(triple) _messageFlow.emit(triple)
// emit()
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Something went wrong when adding temporary message", e) Log.e(TAG, "Something went wrong when adding temporary message", e)
emit(Result.failure(e)) emit(Result.failure(e))

View file

@ -151,8 +151,6 @@ class MessageInputViewModel @Inject constructor(
replyTo: Int, replyTo: Int,
sendWithoutNotification: Boolean sendWithoutNotification: Boolean
) { ) {
// TODO: add temporary message with ref id
val referenceId = SendMessageUtils().generateReferenceId() val referenceId = SendMessageUtils().generateReferenceId()
Log.d(TAG, "Random SHA-256 Hash: $referenceId") Log.d(TAG, "Random SHA-256 Hash: $referenceId")
@ -173,7 +171,6 @@ class MessageInputViewModel @Inject constructor(
} }
} }
if (isQueueing) { if (isQueueing) {
val tempID = System.currentTimeMillis().toInt() val tempID = System.currentTimeMillis().toInt()
val qMsg = QueuedMessage(tempID, message, displayName, replyTo, sendWithoutNotification) val qMsg = QueuedMessage(tempID, message, displayName, replyTo, sendWithoutNotification)

View file

@ -99,6 +99,7 @@ interface ChatMessagesDao {
SELECT * SELECT *
FROM ChatMessages FROM ChatMessages
WHERE internalConversationId = :internalConversationId AND id >= :messageId WHERE internalConversationId = :internalConversationId AND id >= :messageId
AND isTemporary = 0
ORDER BY timestamp ASC, id ASC ORDER BY timestamp ASC, id ASC
""" """
) )
@ -109,6 +110,7 @@ interface ChatMessagesDao {
SELECT * SELECT *
FROM ChatMessages FROM ChatMessages
WHERE internalConversationId = :internalConversationId WHERE internalConversationId = :internalConversationId
AND isTemporary = 0
AND id < :messageId AND id < :messageId
ORDER BY timestamp DESC, id DESC ORDER BY timestamp DESC, id DESC
LIMIT :limit LIMIT :limit
@ -125,6 +127,7 @@ interface ChatMessagesDao {
SELECT * SELECT *
FROM ChatMessages FROM ChatMessages
WHERE internalConversationId = :internalConversationId WHERE internalConversationId = :internalConversationId
AND isTemporary = 0
AND id <= :messageId AND id <= :messageId
ORDER BY timestamp DESC, id DESC ORDER BY timestamp DESC, id DESC
LIMIT :limit LIMIT :limit
@ -141,6 +144,7 @@ interface ChatMessagesDao {
SELECT COUNT(*) SELECT COUNT(*)
FROM ChatMessages FROM ChatMessages
WHERE internalConversationId = :internalConversationId WHERE internalConversationId = :internalConversationId
AND isTemporary = 0
AND id BETWEEN :newestMessageId AND :oldestMessageId AND id BETWEEN :newestMessageId AND :oldestMessageId
""" """
) )