mirror of
https://github.com/nextcloud/talk-android
synced 2025-07-06 20:39:47 +01:00
add chatBlock handling for threads
Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
parent
7d6cdb9e0d
commit
b04a9c49cf
@ -52,7 +52,8 @@ class ChatBlocksDaoTest {
|
||||
@Test
|
||||
fun testGetConnectedChatBlocks() =
|
||||
runTest {
|
||||
usersDao.saveUser(createUserEntity("account1", "Account 1"))
|
||||
val user = createUserEntity("account1", "Account 1")
|
||||
usersDao.saveUser(user)
|
||||
val account1 = usersDao.getUserWithUserId("account1").blockingGet()
|
||||
|
||||
conversationsDao.upsertConversations(
|
||||
@ -77,6 +78,7 @@ class ChatBlocksDaoTest {
|
||||
internalConversationId = conversation1.internalId,
|
||||
accountId = conversation1.accountId,
|
||||
token = conversation1.token,
|
||||
threadId = null,
|
||||
oldestMessageId = 50,
|
||||
newestMessageId = 60,
|
||||
hasHistory = true
|
||||
@ -86,6 +88,7 @@ class ChatBlocksDaoTest {
|
||||
internalConversationId = conversation1.internalId,
|
||||
accountId = conversation1.accountId,
|
||||
token = conversation1.token,
|
||||
threadId = null,
|
||||
oldestMessageId = 10,
|
||||
newestMessageId = 20,
|
||||
hasHistory = true
|
||||
@ -95,6 +98,7 @@ class ChatBlocksDaoTest {
|
||||
internalConversationId = conversation1.internalId,
|
||||
accountId = conversation1.accountId,
|
||||
token = conversation1.token,
|
||||
threadId = null,
|
||||
oldestMessageId = 45,
|
||||
newestMessageId = 55,
|
||||
hasHistory = true
|
||||
@ -104,6 +108,7 @@ class ChatBlocksDaoTest {
|
||||
internalConversationId = conversation1.internalId,
|
||||
accountId = conversation1.accountId,
|
||||
token = conversation1.token,
|
||||
threadId = null,
|
||||
oldestMessageId = 52,
|
||||
newestMessageId = 58,
|
||||
hasHistory = true
|
||||
@ -113,6 +118,7 @@ class ChatBlocksDaoTest {
|
||||
internalConversationId = conversation1.internalId,
|
||||
accountId = conversation1.accountId,
|
||||
token = conversation1.token,
|
||||
threadId = null,
|
||||
oldestMessageId = 1,
|
||||
newestMessageId = 99,
|
||||
hasHistory = true
|
||||
@ -122,6 +128,7 @@ class ChatBlocksDaoTest {
|
||||
internalConversationId = conversation1.internalId,
|
||||
accountId = conversation1.accountId,
|
||||
token = conversation1.token,
|
||||
threadId = null,
|
||||
oldestMessageId = 59,
|
||||
newestMessageId = 70,
|
||||
hasHistory = true
|
||||
@ -131,6 +138,7 @@ class ChatBlocksDaoTest {
|
||||
internalConversationId = conversation1.internalId,
|
||||
accountId = conversation1.accountId,
|
||||
token = conversation1.token,
|
||||
threadId = null,
|
||||
oldestMessageId = 80,
|
||||
newestMessageId = 90,
|
||||
hasHistory = true
|
||||
@ -140,6 +148,7 @@ class ChatBlocksDaoTest {
|
||||
internalConversationId = conversation2.internalId,
|
||||
accountId = conversation2.accountId,
|
||||
token = conversation2.token,
|
||||
threadId = null,
|
||||
oldestMessageId = 53,
|
||||
newestMessageId = 57,
|
||||
hasHistory = true
|
||||
@ -156,9 +165,10 @@ class ChatBlocksDaoTest {
|
||||
chatBlocksDao.upsertChatBlock(chatBlockWithinButOtherConversation)
|
||||
|
||||
val results = chatBlocksDao.getConnectedChatBlocks(
|
||||
conversation1.internalId,
|
||||
searchedChatBlock.oldestMessageId,
|
||||
searchedChatBlock.newestMessageId
|
||||
internalConversationId = conversation1.internalId,
|
||||
threadId = null,
|
||||
oldestMessageId = searchedChatBlock.oldestMessageId,
|
||||
newestMessageId = searchedChatBlock.newestMessageId
|
||||
)
|
||||
|
||||
assertEquals(5, results.first().size)
|
||||
|
@ -140,7 +140,11 @@ class ChatMessagesDaoTest {
|
||||
assertEquals("are", conv1chatMessage3.message)
|
||||
|
||||
val chatMessagesConv1Since =
|
||||
chatMessagesDao.getMessagesForConversationSince(conversation1.internalId, conv1chatMessage3.id)
|
||||
chatMessagesDao.getMessagesForConversationSince(
|
||||
conversation1.internalId,
|
||||
conv1chatMessage3.id,
|
||||
null
|
||||
)
|
||||
assertEquals(3, chatMessagesConv1Since.first().size)
|
||||
assertEquals("are", chatMessagesConv1Since.first()[0].message)
|
||||
assertEquals("some", chatMessagesConv1Since.first()[1].message)
|
||||
@ -150,7 +154,8 @@ class ChatMessagesDaoTest {
|
||||
chatMessagesDao.getMessagesForConversationBeforeAndEqual(
|
||||
conversation1.internalId,
|
||||
conv1chatMessage3.id,
|
||||
3
|
||||
3,
|
||||
null
|
||||
)
|
||||
assertEquals(3, chatMessagesConv1To.first().size)
|
||||
assertEquals("hello", chatMessagesConv1To.first()[2].message)
|
||||
|
@ -10,6 +10,7 @@
|
||||
package com.nextcloud.talk.adapters.messages
|
||||
|
||||
import android.content.Context
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
@ -145,7 +146,10 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) :
|
||||
binding.messageAuthor.visibility = View.GONE
|
||||
}
|
||||
binding.messageText.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize)
|
||||
binding.messageText.text = processedMessageText
|
||||
// binding.messageText.text = processedMessageText
|
||||
// just for debugging:
|
||||
binding.messageText.text =
|
||||
SpannableStringBuilder(processedMessageText).append(" (" + message.jsonMessageId + ")")
|
||||
} else {
|
||||
binding.messageText.visibility = View.GONE
|
||||
binding.checkboxContainer.visibility = View.VISIBLE
|
||||
|
@ -10,6 +10,7 @@
|
||||
package com.nextcloud.talk.adapters.messages
|
||||
|
||||
import android.content.Context
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
@ -159,7 +160,10 @@ class OutcomingTextMessageViewHolder(itemView: View) :
|
||||
|
||||
binding.messageTime.layoutParams = layoutParams
|
||||
viewThemeUtils.platform.colorTextView(binding.messageText, ColorRole.ON_SURFACE_VARIANT)
|
||||
binding.messageText.text = processedMessageText
|
||||
// binding.messageText.text = processedMessageText
|
||||
// just for debugging:
|
||||
binding.messageText.text =
|
||||
SpannableStringBuilder(processedMessageText).append(" (" + message.jsonMessageId + ")")
|
||||
} else {
|
||||
binding.messageText.visibility = View.GONE
|
||||
binding.checkboxContainer.visibility = View.VISIBLE
|
||||
|
@ -557,7 +557,12 @@ class ChatActivity :
|
||||
val extras: Bundle? = intent.extras
|
||||
|
||||
roomToken = extras?.getString(KEY_ROOM_TOKEN).orEmpty()
|
||||
threadId = extras?.getLong(KEY_THREAD_ID)
|
||||
|
||||
threadId = if (extras?.containsKey(KEY_THREAD_ID) == true) {
|
||||
extras.getLong(KEY_THREAD_ID)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
sharedText = extras?.getString(BundleKeys.KEY_SHARED_TEXT).orEmpty()
|
||||
|
||||
|
@ -45,9 +45,11 @@ data class ChatMessage(
|
||||
|
||||
var token: String? = null,
|
||||
|
||||
var topmostParentId: Long? = null,
|
||||
var threadId: Long? = null,
|
||||
|
||||
var childrenCount: Long? = 0,
|
||||
var isThread: Boolean = false,
|
||||
|
||||
// var childrenCount: Long? = 0,
|
||||
|
||||
// guests or users
|
||||
var actorType: String? = null,
|
||||
|
@ -122,16 +122,10 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
private var threadId: Long? = null
|
||||
|
||||
override fun initData(credentials: String, urlForChatting: String, roomToken: String, threadId: Long?) {
|
||||
val threadIdAppendedString = if (threadId != null && threadId > 0) {
|
||||
"@$threadId"
|
||||
} else {
|
||||
""
|
||||
}
|
||||
internalConversationId = currentUser.id.toString() + "@" + roomToken + threadIdAppendedString
|
||||
internalConversationId = currentUser.id.toString() + "@" + roomToken
|
||||
this.credentials = credentials
|
||||
this.urlForChatting = urlForChatting
|
||||
this.threadId = threadId // use this threadId in API requests when fetching messages? +
|
||||
// Introduce ChatBlocks for threads
|
||||
this.threadId = threadId
|
||||
}
|
||||
|
||||
override fun updateConversation(conversationModel: ConversationModel) {
|
||||
@ -151,7 +145,8 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
Log.d(TAG, "conversationModel.internalId: " + conversationModel.internalId)
|
||||
Log.d(TAG, "conversationModel.lastReadMessage:" + conversationModel.lastReadMessage)
|
||||
|
||||
var newestMessageIdFromDb = chatDao.getNewestMessageId(internalConversationId)
|
||||
// var newestMessageIdFromDb = chatDao.getNewestMessageId(internalConversationId, threadId)
|
||||
var newestMessageIdFromDb = chatBlocksDao.getNewestMessageIdFromChatBlocks(internalConversationId, threadId)
|
||||
Log.d(TAG, "newestMessageIdFromDb: $newestMessageIdFromDb")
|
||||
|
||||
val weAlreadyHaveSomeOfflineMessages = newestMessageIdFromDb > 0
|
||||
@ -197,7 +192,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
Log.e(TAG, "initial loading of messages failed")
|
||||
}
|
||||
|
||||
newestMessageIdFromDb = chatDao.getNewestMessageId(internalConversationId)
|
||||
newestMessageIdFromDb = chatBlocksDao.getNewestMessageIdFromChatBlocks(internalConversationId, threadId)
|
||||
Log.d(TAG, "newestMessageIdFromDb after sync: $newestMessageIdFromDb")
|
||||
}
|
||||
|
||||
@ -211,9 +206,9 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
val limit = getCappedMessagesAmountOfChatBlock(newestMessageIdFromDb)
|
||||
|
||||
val list = getMessagesBeforeAndEqual(
|
||||
newestMessageIdFromDb,
|
||||
internalConversationId,
|
||||
limit
|
||||
messageId = newestMessageIdFromDb,
|
||||
internalConversationId = internalConversationId,
|
||||
messageLimit = limit
|
||||
)
|
||||
if (list.isNotEmpty()) {
|
||||
handleNewAndTempMessages(
|
||||
@ -242,7 +237,8 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
val amountBetween = chatDao.getCountBetweenMessageIds(
|
||||
internalConversationId,
|
||||
messageId,
|
||||
chatBlock.oldestMessageId
|
||||
chatBlock.oldestMessageId,
|
||||
threadId
|
||||
)
|
||||
|
||||
Log.d(TAG, "amount of messages between newestMessageId and oldest message of same ChatBlock:$amountBetween")
|
||||
@ -292,7 +288,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
)
|
||||
withNetworkParams.putSerializable(BundleKeys.KEY_FIELD_MAP, fieldMap)
|
||||
|
||||
val loadFromServer = hasToLoadPreviousMessagesFromServer(beforeMessageId)
|
||||
val loadFromServer = hasToLoadPreviousMessagesFromServer(beforeMessageId, DEFAULT_MESSAGES_LIMIT)
|
||||
|
||||
if (loadFromServer) {
|
||||
Log.d(TAG, "Starting online request for loadMoreMessages")
|
||||
@ -354,7 +350,10 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
|
||||
updateUiForLastCommonRead()
|
||||
|
||||
val newestMessage = chatDao.getNewestMessageId(internalConversationId).toInt()
|
||||
val newestMessage = chatBlocksDao.getNewestMessageIdFromChatBlocks(
|
||||
internalConversationId,
|
||||
threadId
|
||||
).toInt()
|
||||
|
||||
// update field map vars for next cycle
|
||||
fieldMap = getFieldMap(
|
||||
@ -380,7 +379,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
}
|
||||
|
||||
// remove all temp messages from UI
|
||||
val oldTempMessages = chatDao.getTempMessagesForConversation(internalConversationId)
|
||||
val oldTempMessages = chatDao.getTempMessagesForConversation(internalConversationId, threadId)
|
||||
.first()
|
||||
.map(ChatMessageEntity::asModel)
|
||||
oldTempMessages.forEach {
|
||||
@ -404,7 +403,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
)
|
||||
|
||||
// add the remaining temp messages to UI again
|
||||
val remainingTempMessages = chatDao.getTempMessagesForConversation(internalConversationId)
|
||||
val remainingTempMessages = chatDao.getTempMessagesForConversation(internalConversationId, threadId)
|
||||
.first()
|
||||
.sortedBy { it.internalId }
|
||||
.map(ChatMessageEntity::asModel)
|
||||
@ -417,7 +416,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
_messageFlow.emit(triple)
|
||||
}
|
||||
|
||||
private suspend fun hasToLoadPreviousMessagesFromServer(beforeMessageId: Long): Boolean {
|
||||
private suspend fun hasToLoadPreviousMessagesFromServer(beforeMessageId: Long, amountToCheck: Int): Boolean {
|
||||
val loadFromServer: Boolean
|
||||
|
||||
val blockForMessage = getBlockOfMessage(beforeMessageId.toInt())
|
||||
@ -429,21 +428,19 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
Log.d(TAG, "The last chatBlock is reached so we won't request server for older messages")
|
||||
loadFromServer = false
|
||||
} else {
|
||||
// we know that beforeMessageId and blockForMessage.oldestMessageId are in the same block.
|
||||
// As we want the last DEFAULT_MESSAGES_LIMIT entries before beforeMessageId, we calculate if these
|
||||
// messages are DEFAULT_MESSAGES_LIMIT entries apart from each other
|
||||
|
||||
val amountBetween = chatDao.getCountBetweenMessageIds(
|
||||
internalConversationId,
|
||||
beforeMessageId,
|
||||
blockForMessage.oldestMessageId
|
||||
blockForMessage.oldestMessageId,
|
||||
threadId
|
||||
)
|
||||
loadFromServer = amountBetween < DEFAULT_MESSAGES_LIMIT
|
||||
loadFromServer = amountBetween < amountToCheck
|
||||
|
||||
Log.d(
|
||||
TAG,
|
||||
"Amount between messageId " + beforeMessageId + " and " + blockForMessage.oldestMessageId +
|
||||
" is: " + amountBetween + " so 'loadFromServer' is " + loadFromServer
|
||||
" is: " + amountBetween + " and $amountToCheck were needed, so 'loadFromServer' is " +
|
||||
loadFromServer
|
||||
)
|
||||
}
|
||||
return loadFromServer
|
||||
@ -479,7 +476,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
|
||||
override suspend fun getMessage(messageId: Long, bundle: Bundle): Flow<ChatMessage> {
|
||||
Log.d(TAG, "Get message with id $messageId")
|
||||
val loadFromServer = hasToLoadPreviousMessagesFromServer(messageId)
|
||||
val loadFromServer = hasToLoadPreviousMessagesFromServer(messageId, 1)
|
||||
|
||||
if (loadFromServer) {
|
||||
val fieldMap = getFieldMap(
|
||||
@ -495,8 +492,10 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
Log.d(TAG, "Starting online request for single message (e.g. a reply)")
|
||||
sync(bundle)
|
||||
}
|
||||
return chatDao.getChatMessageForConversation(internalConversationId, messageId)
|
||||
.map(ChatMessageEntity::asModel)
|
||||
return chatDao.getChatMessageForConversation(
|
||||
internalConversationId,
|
||||
messageId
|
||||
).map(ChatMessageEntity::asModel)
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST", "MagicNumber", "Detekt.TooGenericExceptionCaught")
|
||||
@ -660,11 +659,12 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
internalConversationId = internalConversationId,
|
||||
accountId = conversationModel.accountId,
|
||||
token = conversationModel.token,
|
||||
threadId = threadId,
|
||||
oldestMessageId = oldestMessageIdForNewChatBlock,
|
||||
newestMessageId = newestMessageIdForNewChatBlock,
|
||||
hasHistory = hasHistory
|
||||
)
|
||||
chatBlocksDao.upsertChatBlock(newChatBlock)
|
||||
chatBlocksDao.upsertChatBlock(newChatBlock) // crash when no conversation thread exists!
|
||||
|
||||
updateBlocks(newChatBlock)
|
||||
return chatMessagesFromSyncToProcess
|
||||
@ -721,7 +721,11 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
var blockContainingQueriedMessage: ChatBlockEntity? = null
|
||||
if (queriedMessageId != null) {
|
||||
val blocksContainingQueriedMessage =
|
||||
chatBlocksDao.getChatBlocksContainingMessageId(internalConversationId, queriedMessageId.toLong())
|
||||
chatBlocksDao.getChatBlocksContainingMessageId(
|
||||
internalConversationId = internalConversationId,
|
||||
threadId = threadId,
|
||||
messageId = queriedMessageId.toLong()
|
||||
)
|
||||
|
||||
val chatBlocks = blocksContainingQueriedMessage.first()
|
||||
if (chatBlocks.size > 1) {
|
||||
@ -740,9 +744,10 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
private suspend fun updateBlocks(chatBlock: ChatBlockEntity): ChatBlockEntity? {
|
||||
val connectedChatBlocks =
|
||||
chatBlocksDao.getConnectedChatBlocks(
|
||||
internalConversationId,
|
||||
chatBlock.oldestMessageId,
|
||||
chatBlock.newestMessageId
|
||||
internalConversationId = internalConversationId,
|
||||
threadId = threadId,
|
||||
oldestMessageId = chatBlock.oldestMessageId,
|
||||
newestMessageId = chatBlock.newestMessageId
|
||||
).first()
|
||||
|
||||
return if (connectedChatBlocks.size == 1) {
|
||||
@ -769,7 +774,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
internalConversationId = internalConversationId,
|
||||
accountId = conversationModel.accountId,
|
||||
token = conversationModel.token,
|
||||
threadId = conversationModel.threadId,
|
||||
threadId = threadId,
|
||||
oldestMessageId = oldestIdFromDbChatBlocks,
|
||||
newestMessageId = newestIdFromDbChatBlocks,
|
||||
hasHistory = hasHistory
|
||||
@ -793,7 +798,8 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
chatDao.getMessagesForConversationBeforeAndEqual(
|
||||
internalConversationId,
|
||||
messageId,
|
||||
messageLimit
|
||||
messageLimit,
|
||||
threadId
|
||||
).map {
|
||||
it.map(ChatMessageEntity::asModel)
|
||||
}.first()
|
||||
@ -807,7 +813,8 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
chatDao.getMessagesForConversationBefore(
|
||||
internalConversationId,
|
||||
messageId,
|
||||
messageLimit
|
||||
messageLimit,
|
||||
threadId
|
||||
).map {
|
||||
it.map(ChatMessageEntity::asModel)
|
||||
}.first()
|
||||
@ -870,7 +877,8 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
|
||||
val sentMessage = chatDao.getTempMessageForConversation(
|
||||
internalConversationId,
|
||||
referenceId
|
||||
referenceId,
|
||||
threadId
|
||||
).firstOrNull()
|
||||
|
||||
sentMessage?.let {
|
||||
@ -886,7 +894,8 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
|
||||
val failedMessage = chatDao.getTempMessageForConversation(
|
||||
internalConversationId,
|
||||
referenceId
|
||||
referenceId,
|
||||
threadId
|
||||
).firstOrNull()
|
||||
failedMessage?.let {
|
||||
it.sendStatus = SendStatus.FAILED
|
||||
@ -909,7 +918,11 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
sendWithoutNotification: Boolean,
|
||||
referenceId: String
|
||||
): Flow<Result<ChatMessage?>> {
|
||||
val messageToResend = chatDao.getTempMessageForConversation(internalConversationId, referenceId).firstOrNull()
|
||||
val messageToResend = chatDao.getTempMessageForConversation(
|
||||
internalConversationId,
|
||||
referenceId,
|
||||
threadId
|
||||
).firstOrNull()
|
||||
return if (messageToResend != null) {
|
||||
messageToResend.sendStatus = SendStatus.PENDING
|
||||
chatDao.updateChatMessage(messageToResend)
|
||||
@ -958,8 +971,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
try {
|
||||
val messageToEdit = chatDao.getChatMessageForConversation(
|
||||
internalConversationId,
|
||||
message.jsonMessageId
|
||||
.toLong()
|
||||
message.jsonMessageId.toLong()
|
||||
).first()
|
||||
messageToEdit.message = editedMessageText
|
||||
chatDao.upsertChatMessage(messageToEdit)
|
||||
@ -973,7 +985,7 @@ class OfflineFirstChatRepository @Inject constructor(
|
||||
}
|
||||
|
||||
override suspend fun sendUnsentChatMessages(credentials: String, url: String) {
|
||||
val tempMessages = chatDao.getTempUnsentMessagesForConversation(internalConversationId).first()
|
||||
val tempMessages = chatDao.getTempUnsentMessagesForConversation(internalConversationId, threadId).first()
|
||||
tempMessages.sortedBy { it.internalId }.onEach {
|
||||
sendChatMessage(
|
||||
credentials,
|
||||
|
@ -20,15 +20,17 @@ interface ChatBlocksDao {
|
||||
@Delete
|
||||
fun deleteChatBlocks(blocks: List<ChatBlockEntity>)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT *
|
||||
FROM ChatBlocks
|
||||
WHERE internalConversationId in (:internalConversationId)
|
||||
ORDER BY newestMessageId ASC
|
||||
"""
|
||||
)
|
||||
fun getChatBlocks(internalConversationId: String): Flow<List<ChatBlockEntity>>
|
||||
// @Query(
|
||||
// """
|
||||
// SELECT *
|
||||
// FROM ChatBlocks
|
||||
// WHERE internalConversationId in (:internalConversationId)
|
||||
// ORDER BY newestMessageId ASC
|
||||
// """
|
||||
// )
|
||||
// fun getChatBlocks(
|
||||
// internalConversationId: String
|
||||
// ): Flow<List<ChatBlockEntity>>
|
||||
|
||||
// @Query(
|
||||
// """
|
||||
@ -50,18 +52,24 @@ interface ChatBlocksDao {
|
||||
SELECT *
|
||||
FROM ChatBlocks
|
||||
WHERE internalConversationId in (:internalConversationId)
|
||||
AND (:threadId IS NULL OR threadId = :threadId)
|
||||
AND oldestMessageId <= :messageId
|
||||
AND newestMessageId >= :messageId
|
||||
ORDER BY newestMessageId ASC
|
||||
"""
|
||||
)
|
||||
fun getChatBlocksContainingMessageId(internalConversationId: String, messageId: Long): Flow<List<ChatBlockEntity?>>
|
||||
fun getChatBlocksContainingMessageId(
|
||||
internalConversationId: String,
|
||||
threadId: Long?,
|
||||
messageId: Long
|
||||
): Flow<List<ChatBlockEntity?>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT *
|
||||
FROM ChatBlocks
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND (:threadId IS NULL OR threadId = :threadId)
|
||||
AND(
|
||||
(oldestMessageId <= :oldestMessageId AND newestMessageId >= :oldestMessageId)
|
||||
OR
|
||||
@ -74,20 +82,23 @@ interface ChatBlocksDao {
|
||||
)
|
||||
fun getConnectedChatBlocks(
|
||||
internalConversationId: String,
|
||||
threadId: Long?,
|
||||
oldestMessageId: Long,
|
||||
newestMessageId: Long
|
||||
): Flow<List<ChatBlockEntity>>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsertChatBlock(chatBlock: ChatBlockEntity)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
DELETE FROM ChatBlocks
|
||||
WHERE internalConversationId LIKE :pattern
|
||||
SELECT MAX(newestMessageId) as max_items
|
||||
FROM ChatBlocks
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND (:threadId IS NULL OR threadId = :threadId)
|
||||
"""
|
||||
)
|
||||
fun clearChatBlocksForUser(pattern: String)
|
||||
fun getNewestMessageIdFromChatBlocks(internalConversationId: String, threadId: Long?): Long
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsertChatBlock(chatBlock: ChatBlockEntity)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
|
@ -18,15 +18,19 @@ import kotlinx.coroutines.flow.Flow
|
||||
@Dao
|
||||
@Suppress("Detekt.TooManyFunctions")
|
||||
interface ChatMessagesDao {
|
||||
@Query(
|
||||
"""
|
||||
SELECT MAX(id) as max_items
|
||||
FROM ChatMessages
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND isTemporary = 0
|
||||
"""
|
||||
)
|
||||
fun getNewestMessageId(internalConversationId: String): Long
|
||||
// @Query(
|
||||
// """
|
||||
// SELECT MAX(id) as max_items
|
||||
// FROM ChatMessages
|
||||
// WHERE internalConversationId = :internalConversationId
|
||||
// AND isTemporary = 0
|
||||
// AND (:threadId IS NULL OR threadId = :threadId)
|
||||
// """
|
||||
// )
|
||||
// fun getNewestMessageId(
|
||||
// internalConversationId: String,
|
||||
// threadId: Long?
|
||||
// ): Long
|
||||
|
||||
@Query(
|
||||
"""
|
||||
@ -45,10 +49,11 @@ interface ChatMessagesDao {
|
||||
FROM ChatMessages
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND isTemporary = 1
|
||||
AND (:threadId IS NULL OR threadId = :threadId)
|
||||
ORDER BY timestamp DESC, id DESC
|
||||
"""
|
||||
)
|
||||
fun getTempMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>>
|
||||
fun getTempMessagesForConversation(internalConversationId: String, threadId: Long?): Flow<List<ChatMessageEntity>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
@ -57,10 +62,14 @@ interface ChatMessagesDao {
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND isTemporary = 1
|
||||
AND sendStatus != 'SENT_PENDING_ACK'
|
||||
AND (:threadId IS NULL OR threadId = :threadId)
|
||||
ORDER BY timestamp DESC, id DESC
|
||||
"""
|
||||
)
|
||||
fun getTempUnsentMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>>
|
||||
fun getTempUnsentMessagesForConversation(
|
||||
internalConversationId: String,
|
||||
threadId: Long?
|
||||
): Flow<List<ChatMessageEntity>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
@ -69,10 +78,15 @@ interface ChatMessagesDao {
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND referenceId = :referenceId
|
||||
AND isTemporary = 1
|
||||
AND (:threadId IS NULL OR threadId = :threadId)
|
||||
ORDER BY timestamp DESC, id DESC
|
||||
"""
|
||||
)
|
||||
fun getTempMessageForConversation(internalConversationId: String, referenceId: String): Flow<ChatMessageEntity?>
|
||||
fun getTempMessageForConversation(
|
||||
internalConversationId: String,
|
||||
referenceId: String,
|
||||
threadId: Long?
|
||||
): Flow<ChatMessageEntity?>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsertChatMessages(chatMessages: List<ChatMessageEntity>)
|
||||
@ -84,7 +98,8 @@ interface ChatMessagesDao {
|
||||
"""
|
||||
SELECT *
|
||||
FROM ChatMessages
|
||||
WHERE internalConversationId = :internalConversationId AND id = :messageId
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND id = :messageId
|
||||
"""
|
||||
)
|
||||
fun getChatMessageForConversation(internalConversationId: String, messageId: Long): Flow<ChatMessageEntity>
|
||||
@ -126,10 +141,15 @@ interface ChatMessagesDao {
|
||||
FROM ChatMessages
|
||||
WHERE internalConversationId = :internalConversationId AND id >= :messageId
|
||||
AND isTemporary = 0
|
||||
AND (:threadId IS NULL OR threadId = :threadId)
|
||||
ORDER BY timestamp ASC, id ASC
|
||||
"""
|
||||
)
|
||||
fun getMessagesForConversationSince(internalConversationId: String, messageId: Long): Flow<List<ChatMessageEntity>>
|
||||
fun getMessagesForConversationSince(
|
||||
internalConversationId: String,
|
||||
messageId: Long,
|
||||
threadId: Long?
|
||||
): Flow<List<ChatMessageEntity>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
@ -138,6 +158,7 @@ interface ChatMessagesDao {
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND isTemporary = 0
|
||||
AND id < :messageId
|
||||
AND (:threadId IS NULL OR threadId = :threadId)
|
||||
ORDER BY timestamp DESC, id DESC
|
||||
LIMIT :limit
|
||||
"""
|
||||
@ -145,7 +166,8 @@ interface ChatMessagesDao {
|
||||
fun getMessagesForConversationBefore(
|
||||
internalConversationId: String,
|
||||
messageId: Long,
|
||||
limit: Int
|
||||
limit: Int,
|
||||
threadId: Long?
|
||||
): Flow<List<ChatMessageEntity>>
|
||||
|
||||
@Query(
|
||||
@ -155,6 +177,7 @@ interface ChatMessagesDao {
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND isTemporary = 0
|
||||
AND id <= :messageId
|
||||
AND (:threadId IS NULL OR threadId = :threadId)
|
||||
ORDER BY timestamp DESC, id DESC
|
||||
LIMIT :limit
|
||||
"""
|
||||
@ -162,7 +185,8 @@ interface ChatMessagesDao {
|
||||
fun getMessagesForConversationBeforeAndEqual(
|
||||
internalConversationId: String,
|
||||
messageId: Long,
|
||||
limit: Int
|
||||
limit: Int,
|
||||
threadId: Long?
|
||||
): Flow<List<ChatMessageEntity>>
|
||||
|
||||
@Query(
|
||||
@ -171,10 +195,16 @@ interface ChatMessagesDao {
|
||||
FROM ChatMessages
|
||||
WHERE internalConversationId = :internalConversationId
|
||||
AND isTemporary = 0
|
||||
AND (:threadId IS NULL OR threadId = :threadId)
|
||||
AND id BETWEEN :newestMessageId AND :oldestMessageId
|
||||
"""
|
||||
)
|
||||
fun getCountBetweenMessageIds(internalConversationId: String, oldestMessageId: Long, newestMessageId: Long): Int
|
||||
fun getCountBetweenMessageIds(
|
||||
internalConversationId: String,
|
||||
oldestMessageId: Long,
|
||||
newestMessageId: Long,
|
||||
threadId: Long?
|
||||
): Int
|
||||
|
||||
@Query(
|
||||
"""
|
||||
|
@ -19,8 +19,9 @@ fun ChatMessageJson.asEntity(accountId: Long) =
|
||||
accountId = accountId,
|
||||
id = id,
|
||||
internalConversationId = "$accountId@$token",
|
||||
topmostParentId = topmostParentId,
|
||||
childrenCount = childrenCount,
|
||||
threadId = threadId,
|
||||
isThread = hasThread,
|
||||
// childrenCount = childrenCount,
|
||||
message = message!!,
|
||||
token = token!!,
|
||||
actorType = actorType!!,
|
||||
@ -50,8 +51,9 @@ fun ChatMessageEntity.asModel() =
|
||||
jsonMessageId = id.toInt(),
|
||||
message = message,
|
||||
token = token,
|
||||
topmostParentId = topmostParentId,
|
||||
childrenCount = childrenCount,
|
||||
threadId = threadId,
|
||||
isThread = isThread,
|
||||
// childrenCount = childrenCount,
|
||||
actorType = actorType,
|
||||
actorId = actorId,
|
||||
actorDisplayName = actorDisplayName,
|
||||
@ -82,8 +84,9 @@ fun ChatMessageJson.asModel() =
|
||||
jsonMessageId = id.toInt(),
|
||||
message = message,
|
||||
token = token,
|
||||
topmostParentId = topmostParentId,
|
||||
childrenCount = childrenCount,
|
||||
threadId = threadId,
|
||||
isThread = hasThread,
|
||||
// childrenCount = childrenCount,
|
||||
actorType = actorType,
|
||||
actorId = actorId,
|
||||
actorDisplayName = actorDisplayName,
|
||||
|
@ -31,7 +31,7 @@ import androidx.room.PrimaryKey
|
||||
data class ChatBlockEntity(
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
@ColumnInfo(name = "id") var id: Long = 0,
|
||||
// accountId@token(@threadId)
|
||||
// accountId@token
|
||||
@ColumnInfo(name = "internalConversationId") var internalConversationId: String,
|
||||
@ColumnInfo(name = "accountId") var accountId: Long? = null,
|
||||
@ColumnInfo(name = "token") var token: String?,
|
||||
|
@ -41,7 +41,8 @@ data class ChatMessageEntity(
|
||||
@ColumnInfo(name = "id") var id: Long = 0,
|
||||
// accountId@roomtoken
|
||||
@ColumnInfo(name = "internalConversationId") var internalConversationId: String,
|
||||
@ColumnInfo(name = "topmostParentId") var topmostParentId: Long? = null,
|
||||
@ColumnInfo(name = "threadId") var threadId: Long? = null,
|
||||
@ColumnInfo(name = "isThread") var isThread: Boolean = false,
|
||||
@ColumnInfo(name = "actorDisplayName") var actorDisplayName: String,
|
||||
@ColumnInfo(name = "message") var message: String,
|
||||
|
||||
|
@ -49,8 +49,6 @@ data class ConversationEntity(
|
||||
// exactly what we want for this case.
|
||||
@ColumnInfo(name = "token") var token: String,
|
||||
|
||||
@ColumnInfo(name = "threadId") var threadId: Long? = null,
|
||||
|
||||
@ColumnInfo(name = "displayName") var displayName: String,
|
||||
|
||||
// OTHER ATTRIBUTES IN ALPHABETICAL ORDER
|
||||
|
@ -350,18 +350,18 @@ object Migrations {
|
||||
|
||||
db.execSQL(
|
||||
"ALTER TABLE ChatMessages " +
|
||||
"ADD COLUMN topmostParentId INTEGER DEFAULT NULL;"
|
||||
"ADD COLUMN threadId INTEGER DEFAULT NULL;"
|
||||
)
|
||||
|
||||
db.execSQL(
|
||||
"ALTER TABLE ChatMessages " +
|
||||
"ADD COLUMN childrenCount INTEGER DEFAULT 0;"
|
||||
"ADD COLUMN isThread BOOLEAN DEFAULT 0;"
|
||||
)
|
||||
|
||||
db.execSQL(
|
||||
"ALTER TABLE Conversations " +
|
||||
"ADD COLUMN threadId INTEGER DEFAULT NULL;"
|
||||
)
|
||||
// db.execSQL(
|
||||
// "ALTER TABLE ChatMessages " +
|
||||
// "ADD COLUMN childrenCount INTEGER DEFAULT 0;"
|
||||
// )
|
||||
|
||||
// Foreign key constraints are not active during migration.
|
||||
// At least db.execSQL("PRAGMA foreign_keys=ON;") etc did not help.
|
||||
|
@ -17,7 +17,6 @@ class ConversationModel(
|
||||
var internalId: String,
|
||||
var accountId: Long,
|
||||
var token: String,
|
||||
var threadId: Long? = null,
|
||||
var name: String,
|
||||
var displayName: String,
|
||||
var description: String,
|
||||
|
@ -19,8 +19,13 @@ import kotlinx.parcelize.Parcelize
|
||||
data class ChatMessageJson(
|
||||
@JsonField(name = ["id"]) var id: Long = 0,
|
||||
@JsonField(name = ["token"]) var token: String? = null,
|
||||
@JsonField(name = ["topmostParentId"]) var topmostParentId: Long? = null,
|
||||
@JsonField(name = ["childrenCount"]) var childrenCount: Long? = 0,
|
||||
@JsonField(name = ["threadId"]) var threadId: Long? = null,
|
||||
|
||||
// Be aware that variables with "is" at the beginning will lead to the error:
|
||||
// "@JsonField annotation can only be used on private fields if both getter and setter are present."
|
||||
// Instead, name it with "has" at the beginning: isThread -> hasThread
|
||||
@JsonField(name = ["isThread"]) var hasThread: Boolean = false,
|
||||
// @JsonField(name = ["childrenCount"]) var childrenCount: Long? = 0,
|
||||
@JsonField(name = ["actorType"]) var actorType: String? = null,
|
||||
@JsonField(name = ["actorId"]) var actorId: String? = null,
|
||||
@JsonField(name = ["actorDisplayName"]) var actorDisplayName: String? = null,
|
||||
|
@ -28,9 +28,6 @@ data class Conversation(
|
||||
@JsonField(name = ["token"])
|
||||
var token: String = "",
|
||||
|
||||
@JsonField(name = ["threadId"])
|
||||
var threadId: Long? = null,
|
||||
|
||||
@JsonField(name = ["name"])
|
||||
var name: String = "",
|
||||
|
||||
|
@ -101,7 +101,10 @@ class ReactionsRepositoryImpl @Inject constructor(
|
||||
val internalConversationId = "$accountId@$roomToken"
|
||||
val emoji = model.emoji
|
||||
|
||||
val message = dao.getChatMessageForConversation(internalConversationId, id).first()
|
||||
val message = dao.getChatMessageForConversation(
|
||||
internalConversationId,
|
||||
id
|
||||
).first()
|
||||
|
||||
// 2. Check state of entity, create params as needed
|
||||
if (message.reactions == null) {
|
||||
|
@ -23,21 +23,30 @@ import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
class DummyChatMessagesDaoImpl : ChatMessagesDao {
|
||||
override fun getNewestMessageId(internalConversationId: String): Long = 0L
|
||||
// override fun getNewestMessageId(
|
||||
// internalConversationId: String,
|
||||
// threadId: Long?
|
||||
// ): Long = 0L
|
||||
|
||||
override fun getMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>> = flowOf()
|
||||
|
||||
override fun getTempMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>> =
|
||||
flowOf()
|
||||
override fun getTempMessagesForConversation(
|
||||
internalConversationId: String,
|
||||
threadId: Long?
|
||||
): Flow<List<ChatMessageEntity>> = flowOf()
|
||||
|
||||
override fun getTempUnsentMessagesForConversation(internalConversationId: String): Flow<List<ChatMessageEntity>> {
|
||||
override fun getTempUnsentMessagesForConversation(
|
||||
internalConversationId: String,
|
||||
threadId: Long?
|
||||
): Flow<List<ChatMessageEntity>> {
|
||||
// nothing to return here as long this class is only used for the Search window
|
||||
return flowOf()
|
||||
}
|
||||
|
||||
override fun getTempMessageForConversation(
|
||||
internalConversationId: String,
|
||||
referenceId: String
|
||||
referenceId: String,
|
||||
threadId: Long?
|
||||
): Flow<ChatMessageEntity> = flowOf()
|
||||
|
||||
override suspend fun upsertChatMessages(chatMessages: List<ChatMessageEntity>) { /* */ }
|
||||
@ -59,25 +68,29 @@ class DummyChatMessagesDaoImpl : ChatMessagesDao {
|
||||
|
||||
override fun getMessagesForConversationSince(
|
||||
internalConversationId: String,
|
||||
messageId: Long
|
||||
messageId: Long,
|
||||
threadId: Long?
|
||||
): Flow<List<ChatMessageEntity>> = flowOf()
|
||||
|
||||
override fun getMessagesForConversationBefore(
|
||||
internalConversationId: String,
|
||||
messageId: Long,
|
||||
limit: Int
|
||||
limit: Int,
|
||||
threadId: Long?
|
||||
): Flow<List<ChatMessageEntity>> = flowOf()
|
||||
|
||||
override fun getMessagesForConversationBeforeAndEqual(
|
||||
internalConversationId: String,
|
||||
messageId: Long,
|
||||
limit: Int
|
||||
limit: Int,
|
||||
threadId: Long?
|
||||
): Flow<List<ChatMessageEntity>> = flowOf()
|
||||
|
||||
override fun getCountBetweenMessageIds(
|
||||
internalConversationId: String,
|
||||
oldestMessageId: Long,
|
||||
newestMessageId: Long
|
||||
newestMessageId: Long,
|
||||
threadId: Long?
|
||||
): Int = 0
|
||||
|
||||
override fun clearAllMessagesForUser(pattern: String) { /* */ }
|
||||
@ -192,22 +205,28 @@ class DummyConversationDaoImpl : ConversationsDao {
|
||||
class DummyChatBlocksDaoImpl : ChatBlocksDao {
|
||||
override fun deleteChatBlocks(blocks: List<ChatBlockEntity>) { /* */ }
|
||||
|
||||
override fun getChatBlocks(internalConversationId: String): Flow<List<ChatBlockEntity>> = flowOf()
|
||||
// override fun getChatBlocks(
|
||||
// internalConversationId: String
|
||||
// ): Flow<List<ChatBlockEntity>> = flowOf()
|
||||
|
||||
override fun getChatBlocksContainingMessageId(
|
||||
internalConversationId: String,
|
||||
threadId: Long?,
|
||||
messageId: Long
|
||||
): Flow<List<ChatBlockEntity?>> = flowOf()
|
||||
|
||||
override fun getConnectedChatBlocks(
|
||||
internalConversationId: String,
|
||||
threadId: Long?,
|
||||
oldestMessageId: Long,
|
||||
newestMessageId: Long
|
||||
): Flow<List<ChatBlockEntity>> = flowOf()
|
||||
|
||||
override fun getNewestMessageIdFromChatBlocks(internalConversationId: String, threadId: Long?): Long = 0L
|
||||
|
||||
override suspend fun upsertChatBlock(chatBlock: ChatBlockEntity) { /* */ }
|
||||
|
||||
override fun clearChatBlocksForUser(pattern: String) { /* */ }
|
||||
// override fun clearChatBlocksForUser(pattern: String) { /* */ }
|
||||
|
||||
override fun deleteChatBlocksOlderThan(internalConversationId: String, messageId: Long) { /* */ }
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user