implement lastCommonRead handling

contains one workaround for now, see TODO in updateUiForLastCommonRead method

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2024-08-08 21:33:53 +02:00
parent 5bccdada7c
commit 0390c93ed2
No known key found for this signature in database
GPG key ID: C793F8B59F43CE7B
9 changed files with 65 additions and 23 deletions

View file

@ -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')"
]
}
}

View file

@ -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()
}
}

View file

@ -30,6 +30,8 @@ interface ChatMessageRepository : LifecycleAwareManager {
val updateMessageFlow: Flow<ChatMessage>
val lastCommonReadFlow: Flow<Int>
fun setData(
conversationModel: ConversationModel,
credentials: String,

View file

@ -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<ChatMessage> = MutableSharedFlow()
override val lastCommonReadFlow:
Flow<Int>
get() = _lastCommonReadFlow
private val _lastCommonReadFlow:
MutableSharedFlow<Int> = 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,

View file

@ -120,6 +120,8 @@ class ChatViewModel @Inject constructor(
val getUpdateMessageFlow = chatRepository.updateMessageFlow
val getLastCommonReadFlow = chatRepository.lastCommonReadFlow
val getConversationFlow = conversationRepository.conversationFlow
.onEach {
_getRoomViewState.value = GetRoomSuccessState

View file

@ -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,

View file

@ -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,

View file

@ -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,

View file

@ -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,