diff --git a/app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/10.json b/app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/10.json index cda6ebbce..5d5e9dc49 100644 --- a/app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/10.json +++ b/app/schemas/com.nextcloud.talk.data.source.local.TalkDatabase/10.json @@ -2,7 +2,7 @@ "formatVersion": 1, "database": { "version": 10, - "identityHash": "234cdb754d42d9ebf2349763a58a4578", + "identityHash": "1b97b7e937102e4087f8534f1204fe94", "entities": [ { "tableName": "User", @@ -138,7 +138,7 @@ }, { "tableName": "Conversations", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER, `token` TEXT, `name` TEXT, `displayName` TEXT, `description` TEXT, `type` TEXT, `lastPing` INTEGER NOT NULL, `participantType` TEXT, `hasPassword` INTEGER NOT NULL, `sessionId` TEXT, `actorId` TEXT, `actorType` TEXT, `isFavorite` INTEGER NOT NULL, `lastActivity` INTEGER NOT NULL, `unreadMessages` INTEGER NOT NULL, `unreadMention` INTEGER NOT NULL, `lastMessageJson` TEXT, `objectType` TEXT, `notificationLevel` TEXT, `readOnly` TEXT, `lobbyState` TEXT, `lobbyTimer` INTEGER, `lastReadMessage` INTEGER NOT NULL, `hasCall` INTEGER NOT NULL, `callFlag` INTEGER NOT NULL, `canStartCall` INTEGER NOT NULL, `canLeaveConversation` INTEGER, `canDeleteConversation` INTEGER, `unreadMentionDirect` INTEGER, `notificationCalls` INTEGER, `permissions` INTEGER NOT NULL, `messageExpiration` INTEGER NOT NULL, `status` TEXT, `statusIcon` TEXT, `statusMessage` TEXT, `statusClearAt` INTEGER, `callRecording` INTEGER NOT NULL, `avatarVersion` TEXT, `isCustomAvatar` INTEGER, `callStartTime` INTEGER, `recordingConsent` INTEGER NOT NULL, `remoteServer` TEXT, `remoteToken` TEXT, PRIMARY KEY(`internalId`), FOREIGN KEY(`accountId`) REFERENCES `User`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`internalId` TEXT NOT NULL, `accountId` INTEGER, `token` TEXT, `name` TEXT, `displayName` TEXT, `description` TEXT, `type` TEXT, `lastPing` INTEGER NOT NULL, `participantType` TEXT, `hasPassword` INTEGER NOT NULL, `sessionId` TEXT, `actorId` TEXT, `actorType` TEXT, `isFavorite` INTEGER NOT NULL, `lastActivity` INTEGER NOT NULL, `unreadMessages` INTEGER NOT NULL, `unreadMention` INTEGER NOT NULL, `lastMessageJson` TEXT, `objectType` TEXT, `notificationLevel` TEXT, `readOnly` TEXT, `lobbyState` TEXT, `lobbyTimer` INTEGER, `lastReadMessage` INTEGER NOT NULL, `lastCommonReadMessage` INTEGER NOT NULL, `hasCall` INTEGER NOT NULL, `callFlag` INTEGER NOT NULL, `canStartCall` INTEGER NOT NULL, `canLeaveConversation` INTEGER, `canDeleteConversation` INTEGER, `unreadMentionDirect` INTEGER, `notificationCalls` INTEGER, `permissions` INTEGER NOT NULL, `messageExpiration` INTEGER NOT NULL, `status` TEXT, `statusIcon` TEXT, `statusMessage` TEXT, `statusClearAt` INTEGER, `callRecording` INTEGER NOT NULL, `avatarVersion` TEXT, `isCustomAvatar` INTEGER, `callStartTime` INTEGER, `recordingConsent` INTEGER NOT NULL, `remoteServer` TEXT, `remoteToken` TEXT, PRIMARY KEY(`internalId`), FOREIGN KEY(`accountId`) REFERENCES `User`(`id`) ON UPDATE CASCADE ON DELETE CASCADE )", "fields": [ { "fieldPath": "internalId", @@ -284,6 +284,12 @@ "affinity": "INTEGER", "notNull": true }, + { + "fieldPath": "lastCommonReadMessage", + "columnName": "lastCommonReadMessage", + "affinity": "INTEGER", + "notNull": true + }, { "fieldPath": "hasCall", "columnName": "hasCall", @@ -673,7 +679,7 @@ "views": [], "setupQueries": [ "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, '234cdb754d42d9ebf2349763a58a4578')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '1b97b7e937102e4087f8534f1204fe94')" ] } } \ No newline at end of file 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 f53707be4..e6e0f805f 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -834,6 +834,14 @@ class ChatActivity : .collect() } + this.lifecycleScope.launch { + chatViewModel.getLastCommonReadFlow + .onEach { + updateReadStatusOfAllMessages(it) + } + .collect() + } + chatViewModel.reactionDeletedViewState.observe(this) { state -> when (state) { is ChatViewModel.ReactionDeletedSuccessState -> { @@ -2526,6 +2534,7 @@ class ChatActivity : updateReadStatusOfMessage(message, it) } } + adapter!!.notifyDataSetChanged() } } diff --git a/app/src/main/java/com/nextcloud/talk/chat/data/ChatMessageRepository.kt b/app/src/main/java/com/nextcloud/talk/chat/data/ChatMessageRepository.kt index 6b2a43f15..83f79ae5e 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/data/ChatMessageRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/data/ChatMessageRepository.kt @@ -30,6 +30,8 @@ interface ChatMessageRepository : LifecycleAwareManager { val updateMessageFlow: Flow + val lastCommonReadFlow: Flow + fun setData( conversationModel: ConversationModel, credentials: String, 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 ec2d2710e..032f2b455 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 @@ -30,6 +30,7 @@ import io.reactivex.schedulers.Schedulers import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.first @@ -72,6 +73,13 @@ class OfflineFirstChatRepository @Inject constructor( private val _updateMessageFlow: MutableSharedFlow = MutableSharedFlow() + override val lastCommonReadFlow: + Flow + get() = _lastCommonReadFlow + + private val _lastCommonReadFlow: + MutableSharedFlow = MutableSharedFlow() + private var newXChatLastCommonRead: Int? = null private var itIsPaused = false private val scope = CoroutineScope(Dispatchers.IO) @@ -96,6 +104,8 @@ class OfflineFirstChatRepository @Inject constructor( scope.launch { Log.d(TAG, "---- loadInitialMessages ------------") + newXChatLastCommonRead = conversationModel.lastCommonReadMessage + val fieldMap = getFieldMap( lookIntoFuture = false, includeLastKnown = true, @@ -113,10 +123,24 @@ class OfflineFirstChatRepository @Inject constructor( internalConversationId, chatDao.getNewestMessageId(internalConversationId) ) + updateUiForLastCommonRead() initMessagePolling() } + private fun updateUiForLastCommonRead(){ + scope.launch { + // TODO improve... + // delay is a dirty workaround to make sure messages are added to adapter on initial load before setting + // their read status. + // This workaround causes that the checkmarks seem to switch whenever sending a message + delay(200) + newXChatLastCommonRead?.let { + _lastCommonReadFlow.emit(it) + } + } + } + override fun loadMoreMessages( beforeMessageId: Long, roomToken: String, @@ -141,6 +165,7 @@ class OfflineFirstChatRepository @Inject constructor( } showLast100MessagesBefore(internalConversationId, beforeMessageId) + updateUiForLastCommonRead() } override fun initMessagePolling(): Job = @@ -174,6 +199,8 @@ class OfflineFirstChatRepository @Inject constructor( _messageFlow.emit(pair) } + updateUiForLastCommonRead() + // Process read status if not null // val lastKnown = datastore.getLastKnownId(internalConversationId, 0) // list = list.map { chatMessage -> @@ -245,9 +272,9 @@ class OfflineFirstChatRepository @Inject constructor( fieldMap["lastKnownMessageId"] = lastKnown } - // newXChatLastCommonRead?.let { - // fieldMap["lastCommonReadId"] = if (it > 0) it else lastKnown - // } + newXChatLastCommonRead?.let { + fieldMap["lastCommonReadId"] = it + } fieldMap["timeout"] = if (lookIntoFuture) 30 else 0 fieldMap["limit"] = 100 @@ -291,26 +318,13 @@ class OfflineFirstChatRepository @Inject constructor( .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) // .timeout(3, TimeUnit.SECONDS) - .map { + .map { it -> when (it.code()) { HTTP_CODE_OK -> { Log.d(TAG, "getMessagesFromServer HTTP_CODE_OK") - // newXChatLastCommonRead = it.headers()["X-Chat-Last-Common-Read"]?.let { - // Integer.parseInt(it) - // } - // - // val xChatLastGivenHeader: String? = it.headers()["X-Chat-Last-Given"] - // val lastKnownId = if (it.headers().size > 0 && - // xChatLastGivenHeader?.isNotEmpty() == true - // ) { - // xChatLastGivenHeader.toInt() - // } else { - // - // } - // - // // if (lastKnownId > 0) { - // datastore.saveLastKnownId(internalConversationId, lastKnownId) - // // } + newXChatLastCommonRead = it.headers()["X-Chat-Last-Common-Read"]?.let { + Integer.parseInt(it) + } return@map Pair( HTTP_CODE_OK, diff --git a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt index f97d27196..7f06b84cf 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt @@ -120,6 +120,8 @@ class ChatViewModel @Inject constructor( val getUpdateMessageFlow = chatRepository.updateMessageFlow + val getLastCommonReadFlow = chatRepository.lastCommonReadFlow + val getConversationFlow = conversationRepository.conversationFlow .onEach { _getRoomViewState.value = GetRoomSuccessState diff --git a/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt b/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt index 576f8178b..bd3424a3c 100644 --- a/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/data/database/mappers/ConversationMapUtils.kt @@ -38,6 +38,7 @@ fun ConversationModel.asEntity() = lobbyState = lobbyState, lobbyTimer = lobbyTimer, lastReadMessage = lastReadMessage, + lastCommonReadMessage = lastCommonReadMessage, hasCall = hasCall, callFlag = callFlag, canStartCall = canStartCall, @@ -86,6 +87,7 @@ fun ConversationEntity.asModel() = lobbyState = lobbyState, lobbyTimer = lobbyTimer, lastReadMessage = lastReadMessage, + lastCommonReadMessage = lastCommonReadMessage, hasCall = hasCall, callFlag = callFlag, canStartCall = canStartCall, @@ -134,6 +136,7 @@ fun Conversation.asEntity(accountId: Long) = lobbyState = lobbyState, lobbyTimer = lobbyTimer, lastReadMessage = lastReadMessage, + lastCommonReadMessage = lastCommonReadMessage, hasCall = hasCall, callFlag = callFlag, canStartCall = canStartCall, diff --git a/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt b/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt index 116a57c34..d8d99a00d 100644 --- a/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt +++ b/app/src/main/java/com/nextcloud/talk/data/database/model/ConversationEntity.kt @@ -68,6 +68,7 @@ data class ConversationEntity( @ColumnInfo(name = "lobbyState") var lobbyState: ConversationEnums.LobbyState? = null, @ColumnInfo(name = "lobbyTimer") var lobbyTimer: Long? = null, @ColumnInfo(name = "lastReadMessage") var lastReadMessage: Int = 0, + @ColumnInfo(name = "lastCommonReadMessage") var lastCommonReadMessage: Int = 0, @ColumnInfo(name = "hasCall") var hasCall: Boolean = false, @ColumnInfo(name = "callFlag") var callFlag: Int = 0, @ColumnInfo(name = "canStartCall") var canStartCall: Boolean = false, diff --git a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt index b7ad1948f..c752f78e8 100644 --- a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt +++ b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt @@ -41,6 +41,7 @@ class ConversationModel( var lobbyState: ConversationEnums.LobbyState? = null, var lobbyTimer: Long? = null, var lastReadMessage: Int = 0, + var lastCommonReadMessage: Int = 0, var hasCall: Boolean = false, var callFlag: Int = 0, var canStartCall: Boolean = false, @@ -101,6 +102,7 @@ class ConversationModel( lobbyState = conversation.lobbyState?.let { ConversationEnums.LobbyState.valueOf(it.name) }, lobbyTimer = conversation.lobbyTimer, lastReadMessage = conversation.lastReadMessage, + lastCommonReadMessage = conversation.lastCommonReadMessage, hasCall = conversation.hasCall, callFlag = conversation.callFlag, canStartCall = conversation.canStartCall, diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt index 85f5a7cbe..82e1dd087 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt @@ -90,6 +90,9 @@ data class Conversation( @JsonField(name = ["lastReadMessage"]) var lastReadMessage: Int = 0, + @JsonField(name = ["lastCommonReadMessage"]) + var lastCommonReadMessage: Int = 0, + @JsonField(name = ["hasCall"]) var hasCall: Boolean = false,