handle special system messages in ChatRepo instead in UI

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2024-08-07 21:35:12 +02:00
parent db8d7b91d4
commit 85f4d8cd92
No known key found for this signature in database
GPG Key ID: C793F8B59F43CE7B
7 changed files with 38 additions and 71 deletions

View File

@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 10, "version": 10,
"identityHash": "93ef64fac7a9a811c4a3c2f5a6406f87", "identityHash": "234cdb754d42d9ebf2349763a58a4578",
"entities": [ "entities": [
{ {
"tableName": "User", "tableName": "User",
@ -438,7 +438,7 @@
}, },
{ {
"tableName": "ChatMessages", "tableName": "ChatMessages",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER, `token` TEXT, `id` INTEGER NOT NULL, `internalConversationId` TEXT, `actorType` TEXT, `actorId` TEXT, `actorDisplayName` TEXT, `timestamp` INTEGER NOT NULL, `systemMessage` TEXT, `messageType` TEXT, `isReplyable` INTEGER NOT NULL, `message` TEXT, `messageParameters` TEXT, `expirationTimestamp` INTEGER NOT NULL, `parent` INTEGER, `reactions` TEXT, `reactionsSelf` TEXT, `markdown` INTEGER, `lastEditActorType` TEXT, `lastEditActorId` TEXT, `lastEditActorDisplayName` TEXT, `lastEditTimestamp` INTEGER, PRIMARY KEY(`internalId`), FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) ON UPDATE CASCADE ON DELETE CASCADE )", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER, `token` TEXT, `id` INTEGER NOT NULL, `internalConversationId` TEXT, `actorType` TEXT, `actorId` TEXT, `actorDisplayName` TEXT, `timestamp` INTEGER NOT NULL, `systemMessage` TEXT, `messageType` TEXT, `isReplyable` INTEGER NOT NULL, `message` TEXT, `messageParameters` TEXT, `expirationTimestamp` INTEGER NOT NULL, `parent` INTEGER, `reactions` TEXT, `reactionsSelf` TEXT, `markdown` INTEGER, `lastEditActorType` TEXT, `lastEditActorId` TEXT, `lastEditActorDisplayName` TEXT, `lastEditTimestamp` INTEGER, `deleted` INTEGER NOT NULL, PRIMARY KEY(`internalId`), FOREIGN KEY(`internalConversationId`) REFERENCES `Conversations`(`internalId`) ON UPDATE CASCADE ON DELETE CASCADE )",
"fields": [ "fields": [
{ {
"fieldPath": "internalId", "fieldPath": "internalId",
@ -577,6 +577,12 @@
"columnName": "lastEditTimestamp", "columnName": "lastEditTimestamp",
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": false "notNull": false
},
{
"fieldPath": "deleted",
"columnName": "deleted",
"affinity": "INTEGER",
"notNull": true
} }
], ],
"primaryKey": { "primaryKey": {
@ -667,7 +673,7 @@
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '93ef64fac7a9a811c4a3c2f5a6406f87')" "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '234cdb754d42d9ebf2349763a58a4578')"
] ]
} }
} }

View File

@ -829,7 +829,7 @@ class ChatActivity :
this.lifecycleScope.launch { this.lifecycleScope.launch {
chatViewModel.getUpdateMessageFlow chatViewModel.getUpdateMessageFlow
.onEach { .onEach {
updateAdapterForReaction(it) updateMessageInsideAdapter(it)
} }
.collect() .collect()
} }
@ -2923,42 +2923,17 @@ class ChatActivity :
private fun handleSystemMessages(chatMessageList: List<ChatMessage>): List<ChatMessage> { private fun handleSystemMessages(chatMessageList: List<ChatMessage>): List<ChatMessage> {
val chatMessageMap = chatMessageList.map { it.id to it }.toMap().toMutableMap() val chatMessageMap = chatMessageList.map { it.id to it }.toMap().toMutableMap()
val chatMessageIterator = chatMessageMap.iterator() val chatMessageIterator = chatMessageMap.iterator()
while (chatMessageIterator.hasNext()) { while (chatMessageIterator.hasNext()) {
val currentMessage = chatMessageIterator.next() val currentMessage = chatMessageIterator.next()
// setDeletionFlagsAndRemoveInfomessages
if (isInfoMessageAboutDeletion(currentMessage)) {
if (!chatMessageMap.containsKey(currentMessage.value.parentMessageId.toString())) {
// if chatMessageMap doesn't contain message to delete (this happens when lookingIntoFuture),
// the message to delete has to be modified directly inside the adapter
val id = currentMessage.value.parentMessageId.toString()
val index = adapter?.getMessagePositionById(id) ?: 0
if (index > 0) {
val message = adapter?.items?.get(index)?.item as ChatMessage
setMessageAsDeleted(message)
}
} else {
chatMessageMap[currentMessage.value.parentMessageId.toString()]!!.isDeleted = true
}
chatMessageIterator.remove()
} else if (isReactionsMessage(currentMessage)) {
// delete reactions system messages
if (!chatMessageMap.containsKey(currentMessage.value.parentMessageId.toString())) {
// updateAdapterForReaction(currentMessage.value.parentMessage) TODO
}
chatMessageIterator.remove()
} else if (isPollVotedMessage(currentMessage)) {
// delete poll system messages
chatMessageIterator.remove()
} else if (isEditMessage(currentMessage)) {
if (!chatMessageMap.containsKey(currentMessage.value.parentMessageId.toString())) {
// setMessageAsEdited(currentMessage.value.parentMessage) TODO
}
if (isInfoMessageAboutDeletion(currentMessage) ||
isReactionsMessage(currentMessage) ||
isPollVotedMessage(currentMessage) ||
isEditMessage(currentMessage)
) {
chatMessageIterator.remove() chatMessageIterator.remove()
} }
} }
@ -3401,10 +3376,11 @@ class ChatActivity :
adapter?.update(messageTemp) adapter?.update(messageTemp)
} }
private fun updateAdapterForReaction(message: IMessage?) { private fun updateMessageInsideAdapter(message: IMessage?) {
message?.let { message?.let {
val messageTemp = message as ChatMessage val messageTemp = message as ChatMessage
// TODO is this needed?
messageTemp.isOneToOneConversation = messageTemp.isOneToOneConversation =
currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL currentConversation?.type == ConversationEnums.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL
messageTemp.activeUser = conversationUser messageTemp.activeUser = conversationUser

View File

@ -9,8 +9,6 @@ package com.nextcloud.talk.chat.data
import android.os.Bundle import android.os.Bundle
import com.nextcloud.talk.chat.data.io.LifecycleAwareManager import com.nextcloud.talk.chat.data.io.LifecycleAwareManager
import com.nextcloud.talk.models.json.chat.ChatMessageJson
import com.nextcloud.talk.data.sync.Syncable
import com.nextcloud.talk.chat.data.model.ChatMessage import com.nextcloud.talk.chat.data.model.ChatMessage
import com.nextcloud.talk.models.domain.ConversationModel import com.nextcloud.talk.models.domain.ConversationModel
import kotlinx.coroutines.Job import kotlinx.coroutines.Job

View File

@ -415,9 +415,7 @@ class OfflineFirstChatRepository @Inject constructor(
if (result.second.isNotEmpty()) { if (result.second.isNotEmpty()) {
val chatMessagesJson = result.second val chatMessagesJson = result.second
if (lookIntoFuture) { handleUpdateMessages(chatMessagesJson)
handleUpdateMessages(chatMessagesJson)
}
chatMessagesFromSync = chatMessagesJson.map { chatMessagesFromSync = chatMessagesJson.map {
it.asEntity(currentUser.id!!) it.asEntity(currentUser.id!!)
@ -467,7 +465,14 @@ class OfflineFirstChatRepository @Inject constructor(
private suspend fun handleUpdateMessages(messagesJson: List<ChatMessageJson>) { private suspend fun handleUpdateMessages(messagesJson: List<ChatMessageJson>) {
messagesJson.forEach { messageJson -> messagesJson.forEach { messageJson ->
when (messageJson.systemMessageType) { when (messageJson.systemMessageType) {
ChatMessage.SystemMessageType.REACTION -> { ChatMessage.SystemMessageType.REACTION,
ChatMessage.SystemMessageType.REACTION_REVOKED,
ChatMessage.SystemMessageType.REACTION_DELETED,
ChatMessage.SystemMessageType.MESSAGE_DELETED,
ChatMessage.SystemMessageType.POLL_VOTED,
ChatMessage.SystemMessageType.MESSAGE_EDITED -> {
// the parent message is always the newest state, no matter how old the system message is.
// that's why we can just take the parent, update it in DB and update the UI
messageJson.parentMessage?.let { parentMessageJson -> messageJson.parentMessage?.let { parentMessageJson ->
val parentMessageEntity = parentMessageJson.asEntity(currentUser.id!!) val parentMessageEntity = parentMessageJson.asEntity(currentUser.id!!)
chatDao.upsertChatMessage(parentMessageEntity) chatDao.upsertChatMessage(parentMessageEntity)
@ -475,26 +480,6 @@ class OfflineFirstChatRepository @Inject constructor(
} }
} }
ChatMessage.SystemMessageType.REACTION_REVOKED -> {
// TODO
}
ChatMessage.SystemMessageType.REACTION_DELETED -> {
// TODO
}
ChatMessage.SystemMessageType.MESSAGE_DELETED -> {
// TODO
}
ChatMessage.SystemMessageType.POLL_VOTED -> {
// TODO
}
ChatMessage.SystemMessageType.MESSAGE_EDITED -> {
// TODO
}
ChatMessage.SystemMessageType.CLEARED_CHAT -> { ChatMessage.SystemMessageType.CLEARED_CHAT -> {
val pattern = "$internalConversationId%" // LIKE "<accountId>@<conversationId>@%" val pattern = "$internalConversationId%" // LIKE "<accountId>@<conversationId>@%"
chatDao.clearAllMessagesForUser(pattern) chatDao.clearAllMessagesForUser(pattern)

View File

@ -38,7 +38,8 @@ fun ChatMessageJson.asEntity(accountId: Long) =
lastEditActorDisplayName = lastEditActorDisplayName, lastEditActorDisplayName = lastEditActorDisplayName,
lastEditActorId = lastEditActorId, lastEditActorId = lastEditActorId,
lastEditActorType = lastEditActorType, lastEditActorType = lastEditActorType,
lastEditTimestamp = lastEditTimestamp lastEditTimestamp = lastEditTimestamp,
deleted = deleted
) )
fun ChatMessageEntity.asModel() = fun ChatMessageEntity.asModel() =
@ -62,7 +63,8 @@ fun ChatMessageEntity.asModel() =
lastEditActorDisplayName = lastEditActorDisplayName, lastEditActorDisplayName = lastEditActorDisplayName,
lastEditActorId = lastEditActorId, lastEditActorId = lastEditActorId,
lastEditActorType = lastEditActorType, lastEditActorType = lastEditActorType,
lastEditTimestamp = lastEditTimestamp lastEditTimestamp = lastEditTimestamp,
isDeleted = deleted
) )
fun ChatMessageJson.asModel() = fun ChatMessageJson.asModel() =
@ -86,5 +88,6 @@ fun ChatMessageJson.asModel() =
lastEditActorDisplayName = lastEditActorDisplayName, lastEditActorDisplayName = lastEditActorDisplayName,
lastEditActorId = lastEditActorId, lastEditActorId = lastEditActorId,
lastEditActorType = lastEditActorType, lastEditActorType = lastEditActorType,
lastEditTimestamp = lastEditTimestamp lastEditTimestamp = lastEditTimestamp,
isDeleted = deleted
) )

View File

@ -58,6 +58,7 @@ data class ChatMessageEntity(
@ColumnInfo(name = "lastEditActorType") var lastEditActorType: String? = null, @ColumnInfo(name = "lastEditActorType") var lastEditActorType: String? = null,
@ColumnInfo(name = "lastEditActorId") var lastEditActorId: String? = null, @ColumnInfo(name = "lastEditActorId") var lastEditActorId: String? = null,
@ColumnInfo(name = "lastEditActorDisplayName") var lastEditActorDisplayName: String? = null, @ColumnInfo(name = "lastEditActorDisplayName") var lastEditActorDisplayName: String? = null,
@ColumnInfo(name = "lastEditTimestamp") var lastEditTimestamp: Long? = 0 @ColumnInfo(name = "lastEditTimestamp") var lastEditTimestamp: Long? = 0,
@ColumnInfo(name = "deleted") var deleted: Boolean = false,
// TODO: add "silent" // TODO: add "silent"
) )

View File

@ -18,7 +18,7 @@ import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
@JsonObject @JsonObject
data class ChatMessageJson( data class ChatMessageJson(
@JsonField(name = ["id"]) override var id: Long = 0, @JsonField(name = ["id"]) var id: Long = 0,
@JsonField(name = ["token"]) var token: String? = null, @JsonField(name = ["token"]) var token: String? = null,
@JsonField(name = ["actorType"]) var actorType: String? = null, @JsonField(name = ["actorType"]) var actorType: String? = null,
@JsonField(name = ["actorId"]) var actorId: String? = null, @JsonField(name = ["actorId"]) var actorId: String? = null,
@ -43,7 +43,5 @@ data class ChatMessageJson(
@JsonField(name = ["lastEditActorId"]) var lastEditActorId: String? = null, @JsonField(name = ["lastEditActorId"]) var lastEditActorId: String? = null,
@JsonField(name = ["lastEditActorType"]) var lastEditActorType: String? = null, @JsonField(name = ["lastEditActorType"]) var lastEditActorType: String? = null,
@JsonField(name = ["lastEditTimestamp"]) var lastEditTimestamp: Long? = 0, @JsonField(name = ["lastEditTimestamp"]) var lastEditTimestamp: Long? = 0,
@JsonField(name = ["deleted"]) var deleted: Boolean = false,
// override var markedForDeletion: Boolean = "comment_deleted" == messageType ) : Parcelable
override var markedForDeletion: Boolean = false
) : Parcelable, SyncableModel