diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index a5f482d30..5068409a6 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -918,8 +918,7 @@ class ChatActivity : this.lifecycleScope.launch { chatViewModel.getRemoveMessageFlow .onEach { - adapter!!.delete(it) - adapter!!.notifyDataSetChanged() + removeMessageById(it.id) } .collect() } @@ -1065,9 +1064,15 @@ class ChatActivity : } 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) { adapter?.items?.removeAt(index) + adapter?.notifyItemRemoved(index) } } diff --git a/app/src/main/java/com/nextcloud/talk/chat/data/network/OfflineFirstChatRepository.kt b/app/src/main/java/com/nextcloud/talk/chat/data/network/OfflineFirstChatRepository.kt index 419178677..5c1a2bad4 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/data/network/OfflineFirstChatRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/data/network/OfflineFirstChatRepository.kt @@ -178,14 +178,19 @@ class OfflineFirstChatRepository @Inject constructor( if (newestMessageIdFromDb.toInt() != 0) { val limit = getCappedMessagesAmountOfChatBlock(newestMessageIdFromDb) - // TODO: somewhere here also handle temp messages. updateUiMessages(chatMessages, showUnreadMessagesMarker) - - showMessagesBeforeAndEqual( - internalConversationId, + val list = getMessagesBeforeAndEqual( newestMessageIdFromDb, + internalConversationId, 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 // with them (otherwise there is a race condition). @@ -302,7 +307,11 @@ class OfflineFirstChatRepository @Inject constructor( val weHaveMessagesFromOurself = chatMessages.any { it.actorId == currentUser.userId } showUnreadMessagesMarker = showUnreadMessagesMarker && !weHaveMessagesFromOurself - updateUiMessages(chatMessages, showUnreadMessagesMarker) + updateUiMessages( + chatMessages = chatMessages, + lookIntoFuture = true, + showUnreadMessagesMarker = showUnreadMessagesMarker + ) } else { Log.d(TAG, "resultsFromSync are null or empty") } @@ -325,25 +334,30 @@ class OfflineFirstChatRepository @Inject constructor( } } - private suspend fun updateUiMessages(chatMessages : List, showUnreadMessagesMarker: Boolean) { + private suspend fun updateUiMessages( + chatMessages : List, + lookIntoFuture: Boolean, + showUnreadMessagesMarker: Boolean + ) { + // remove all temp messages from UI val oldTempMessages = chatDao.getTempMessagesForConversation(internalConversationId) .first() .map(ChatMessageEntity::asModel) - 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) - + // remove temp messages from DB that are now found in the new messages val chatMessagesReferenceIds = chatMessages.mapTo(HashSet(chatMessages.size)) { it.referenceId } val tempChatMessagesThatCanBeReplaced = oldTempMessages.filter { it.referenceId in chatMessagesReferenceIds } - chatDao.deleteTempChatMessages( internalConversationId, tempChatMessagesThatCanBeReplaced.map { it.referenceId!! } ) + // add the remaining temp messages to UI again val remainingTempMessages = chatDao.getTempMessagesForConversation(internalConversationId) .first() .map(ChatMessageEntity::asModel) @@ -706,31 +720,19 @@ class OfflineFirstChatRepository @Inject constructor( } } - private suspend fun showMessagesBeforeAndEqual(internalConversationId: String, messageId: Long, limit: Int) { - suspend fun getMessagesBeforeAndEqual( - messageId: Long, - internalConversationId: String, - messageLimit: Int - ): List = - chatDao.getMessagesForConversationBeforeAndEqual( - internalConversationId, - messageId, - messageLimit - ).map { - it.map(ChatMessageEntity::asModel) - }.first() - - val list = getMessagesBeforeAndEqual( - messageId, + suspend fun getMessagesBeforeAndEqual( + messageId: Long, + internalConversationId: String, + messageLimit: Int + ): List = + chatDao.getMessagesForConversationBeforeAndEqual( 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) { suspend fun getMessagesBefore( @@ -835,9 +837,6 @@ class OfflineFirstChatRepository @Inject constructor( message.toString(), referenceId ) - // accessing internalConversationId creates UninitializedPropertyException because ChatViewModel and - // MessageInputViewModel use different instances of ChatRepository for now - chatDao.upsertChatMessage(tempChatMessageEntity) @@ -847,9 +846,6 @@ class OfflineFirstChatRepository @Inject constructor( val triple = Triple(true, false, listOf(tempChatMessageModel)) _messageFlow.emit(triple) - - // emit() - } catch (e: Exception) { Log.e(TAG, "Something went wrong when adding temporary message", e) emit(Result.failure(e)) diff --git a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/MessageInputViewModel.kt b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/MessageInputViewModel.kt index 0ca79ffad..c00e89546 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/MessageInputViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/MessageInputViewModel.kt @@ -151,8 +151,6 @@ class MessageInputViewModel @Inject constructor( replyTo: Int, sendWithoutNotification: Boolean ) { - // TODO: add temporary message with ref id - val referenceId = SendMessageUtils().generateReferenceId() Log.d(TAG, "Random SHA-256 Hash: $referenceId") @@ -173,7 +171,6 @@ class MessageInputViewModel @Inject constructor( } } - if (isQueueing) { val tempID = System.currentTimeMillis().toInt() val qMsg = QueuedMessage(tempID, message, displayName, replyTo, sendWithoutNotification) diff --git a/app/src/main/java/com/nextcloud/talk/data/database/dao/ChatMessagesDao.kt b/app/src/main/java/com/nextcloud/talk/data/database/dao/ChatMessagesDao.kt index 6357ce236..a15cdbe1c 100644 --- a/app/src/main/java/com/nextcloud/talk/data/database/dao/ChatMessagesDao.kt +++ b/app/src/main/java/com/nextcloud/talk/data/database/dao/ChatMessagesDao.kt @@ -99,6 +99,7 @@ interface ChatMessagesDao { SELECT * FROM ChatMessages WHERE internalConversationId = :internalConversationId AND id >= :messageId + AND isTemporary = 0 ORDER BY timestamp ASC, id ASC """ ) @@ -109,6 +110,7 @@ interface ChatMessagesDao { SELECT * FROM ChatMessages WHERE internalConversationId = :internalConversationId + AND isTemporary = 0 AND id < :messageId ORDER BY timestamp DESC, id DESC LIMIT :limit @@ -125,6 +127,7 @@ interface ChatMessagesDao { SELECT * FROM ChatMessages WHERE internalConversationId = :internalConversationId + AND isTemporary = 0 AND id <= :messageId ORDER BY timestamp DESC, id DESC LIMIT :limit @@ -141,6 +144,7 @@ interface ChatMessagesDao { SELECT COUNT(*) FROM ChatMessages WHERE internalConversationId = :internalConversationId + AND isTemporary = 0 AND id BETWEEN :newestMessageId AND :oldestMessageId """ )