Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
main.com.sceyt.chatuikit.data.repositories.MessagesRepositoryImpl.kt Maven / Gradle / Ivy
package com.sceyt.chatuikit.data.repositories
import com.sceyt.chat.models.SceytException
import com.sceyt.chat.models.SearchQueryOperator
import com.sceyt.chat.models.message.DeleteMessageType
import com.sceyt.chat.models.message.Message
import com.sceyt.chat.models.message.MessageListFilterKey
import com.sceyt.chat.models.message.MessageListMarker
import com.sceyt.chat.models.message.MessageSearchField
import com.sceyt.chat.models.message.MessageSearchQuery
import com.sceyt.chat.models.message.MessagesListQuery
import com.sceyt.chat.models.message.MessagesListQueryByType
import com.sceyt.chat.operators.ChannelOperator
import com.sceyt.chat.sceyt_callbacks.MessageCallback
import com.sceyt.chat.sceyt_callbacks.MessageMarkCallback
import com.sceyt.chat.sceyt_callbacks.MessagesCallback
import com.sceyt.chatuikit.SceytChatUIKit
import com.sceyt.chatuikit.data.models.SceytPagingResponse
import com.sceyt.chatuikit.data.models.SceytResponse
import com.sceyt.chatuikit.data.models.SendMessageResult
import com.sceyt.chatuikit.data.models.messages.MarkerType
import com.sceyt.chatuikit.data.models.messages.SceytMessage
import com.sceyt.chatuikit.extensions.TAG
import com.sceyt.chatuikit.logger.SceytLog
import com.sceyt.chatuikit.persistence.extensions.safeResume
import com.sceyt.chatuikit.persistence.mappers.toMessage
import com.sceyt.chatuikit.persistence.mappers.toSceytUiMessage
import com.sceyt.chatuikit.persistence.repositories.MessagesRepository
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.suspendCancellableCoroutine
class MessagesRepositoryImpl : MessagesRepository {
private var searchMessageListQuery: MessagesListQuery? = null
/**
* @param conversationId id of current conversation, if is reply in thread, it is the reply message id, else channel id.
* @param lastMessageId conversation last message id.
* @param replyInThread reply message in thread mode.
* @param limit count of messages. */
override suspend fun getPrevMessages(conversationId: Long, lastMessageId: Long, replyInThread: Boolean, limit: Int): SceytResponse> {
return suspendCancellableCoroutine { continuation ->
getQuery(conversationId, replyInThread, limit, true).loadPrev(lastMessageId, object : MessagesCallback {
override fun onResult(messages: MutableList?) {
val result: MutableList = messages?.toMutableList() ?: mutableListOf()
continuation.safeResume(SceytResponse.Success(result.map { it.toSceytUiMessage() }))
}
override fun onError(e: SceytException?) {
if (replyInThread && lastMessageId == 0L)
continuation.safeResume(SceytResponse.Success(arrayListOf()))
else {
continuation.safeResume(SceytResponse.Error(e))
SceytLog.e(TAG, "getPrevMessages error: ${e?.message}")
}
}
})
}
}
/**
* @param conversationId id of current conversation, if is reply in thread, it is the reply message id, else channel id.
* @param lastMessageId conversation last message id.
* @param replyInThread reply message in thread mode.
* @param limit count of messages */
override suspend fun getNextMessages(conversationId: Long, lastMessageId: Long, replyInThread: Boolean, limit: Int): SceytResponse> {
return suspendCancellableCoroutine { continuation ->
getQuery(conversationId, replyInThread, limit, false).loadNext(lastMessageId, object : MessagesCallback {
override fun onResult(messages: MutableList?) {
val result: MutableList = messages?.toMutableList() ?: mutableListOf()
continuation.safeResume(SceytResponse.Success(result.map { it.toSceytUiMessage() }))
}
override fun onError(e: SceytException?) {
if (replyInThread && lastMessageId == 0L)
continuation.safeResume(SceytResponse.Success(arrayListOf()))
else {
continuation.safeResume(SceytResponse.Error(e))
SceytLog.e(TAG, "getNextMessages error: ${e?.message}")
}
}
})
}
}
/**
* @param conversationId id of current conversation, if is reply in thread, it is the reply message id, else channel id.
* @param messageId conversation last message id.
* @param replyInThread reply message in thread mode.
* @param limit count of messages */
override suspend fun getNearMessages(conversationId: Long, messageId: Long, replyInThread: Boolean, limit: Int): SceytResponse> {
return suspendCancellableCoroutine { continuation ->
getQuery(conversationId, replyInThread, limit, true).loadNear(messageId, object : MessagesCallback {
override fun onResult(messages: MutableList?) {
val result: MutableList = messages?.toMutableList() ?: mutableListOf()
continuation.safeResume(SceytResponse.Success(result.map { it.toSceytUiMessage() }))
}
override fun onError(e: SceytException?) {
if (replyInThread && messageId == 0L)
continuation.safeResume(SceytResponse.Success(arrayListOf()))
else {
continuation.safeResume(SceytResponse.Error(e))
SceytLog.e(TAG, "getNearMessages error: ${e?.message}")
}
}
})
}
}
/**
* @param channelId the main channel id.
* @param lastMessageId conversation last message id.
* @param type messages type. */
override suspend fun getMessagesByType(channelId: Long, lastMessageId: Long, type: String): SceytResponse> {
val lastMsgId = if (lastMessageId == 0L) Long.MAX_VALUE else lastMessageId
return suspendCancellableCoroutine { continuation ->
getQueryByType(type, channelId, messagesLoadSize).loadNext(lastMsgId, object : MessagesCallback {
override fun onResult(messages: MutableList?) {
val result: MutableList = messages?.toMutableList() ?: mutableListOf()
continuation.safeResume(SceytResponse.Success(result.map { it.toSceytUiMessage() }))
}
override fun onError(e: SceytException?) {
continuation.safeResume(SceytResponse.Error(e))
SceytLog.e(TAG, "getMessagesByType error: ${e?.message}")
}
})
}
}
override suspend fun loadAllMessagesAfter(conversationId: Long, replyInThread: Boolean,
messageId: Long): Flow>>> = callbackFlow {
val query = getQuery(conversationId, replyInThread, messagesLoadSize, false)
var nextMessageId = messageId
query.loadNext(messageId - 1, object : MessagesCallback {
override fun onResult(messages: MutableList?) {
val result: MutableList = messages?.toMutableList() ?: mutableListOf()
trySend(nextMessageId to SceytResponse.Success(result.map { it.toSceytUiMessage() }))
if (result.size == messagesLoadSize) {
nextMessageId = result.last().id
query.loadNext(nextMessageId, this)
} else channel.close()
}
override fun onError(e: SceytException?) {
trySend(nextMessageId to SceytResponse.Error(e))
channel.close()
SceytLog.e(TAG, "loadAllMessagesAfter error: ${e?.message}")
}
})
awaitClose()
}
override suspend fun loadMessagesById(conversationId: Long, ids: List): SceytResponse> {
return suspendCancellableCoroutine { continuation ->
ChannelOperator.build(conversationId).getMessagesById(ids.toLongArray(), object : MessagesCallback {
override fun onResult(result: MutableList?) {
continuation.safeResume(SceytResponse.Success(result?.map { it.toSceytUiMessage() }.orEmpty()))
}
override fun onError(error: SceytException?) {
continuation.safeResume(SceytResponse.Error(error))
SceytLog.e(TAG, "loadMessages error: ${error?.message}")
}
})
}
}
override suspend fun searchMessages(conversationId: Long, replyInThread: Boolean, query: String): SceytPagingResponse> {
return suspendCancellableCoroutine { continuation ->
val searchField = MessageSearchField.Builder()
.withFilterKey(MessageListFilterKey.FilterKeyBody)
.queryType(SearchQueryOperator.SearchQueryOperatorContains)
.query(query)
.build()
val searchQuery = MessageSearchQuery(listOf(searchField))
searchMessageListQuery = getQueryForSearch(conversationId, replyInThread, messagesLoadSize, searchQuery)
searchMessageListQuery?.loadNext(object : MessagesCallback {
override fun onResult(messages: MutableList?) {
continuation.safeResume(SceytPagingResponse.Success((messages?.map {
it.toSceytUiMessage()
}.orEmpty()), searchMessageListQuery?.hasNext ?: false))
}
override fun onError(error: SceytException?) {
continuation.safeResume(SceytPagingResponse.Error(error))
SceytLog.e(TAG, "searchMessages error: ${error?.message}")
}
})
}
}
override suspend fun loadNextSearchMessages(): SceytPagingResponse> {
return suspendCancellableCoroutine { continuation ->
searchMessageListQuery?.loadNext(object : MessagesCallback {
override fun onResult(messages: MutableList?) {
continuation.safeResume(SceytPagingResponse.Success((messages?.map { it.toSceytUiMessage() }.orEmpty()),
searchMessageListQuery?.hasNext ?: false))
}
override fun onError(error: SceytException?) {
continuation.safeResume(SceytPagingResponse.Error(error))
SceytLog.e(TAG, "loadNextSearchMessages error: ${error?.message}")
}
})
}
}
override suspend fun sendMessageAsFlow(channelId: Long, message: Message) = callbackFlow {
val transformMessage = SceytChatUIKit.messageTransformer?.let {
it.transformToSend(message) ?: return@callbackFlow
} ?: message
val response = sendMessage(channelId, transformMessage, tmpMessageCb = {
trySend(SendMessageResult.TempMessage(it.toSceytUiMessage()))
})
when (response) {
is SceytResponse.Success -> trySend(SendMessageResult.Success(response))
is SceytResponse.Error -> trySend(SendMessageResult.Error(response))
}
channel.close()
awaitClose()
}
override suspend fun sendMessage(
channelId: Long, message: Message,
tmpMessageCb: ((Message) -> Unit)?
): SceytResponse = suspendCancellableCoroutine { continuation ->
SceytLog.i(TAG, "sending message with channelId $channelId, tid: ${message.tid}, body: ${message.body}")
val tmpMessage = ChannelOperator.build(channelId).sendMessage(message, object : MessageCallback {
override fun onResult(message: Message) {
SceytLog.i(TAG, "send message success with tid: ${message.tid}, body: ${message.body}, initialTid: ${message.tid}")
val resultTransformed = SceytChatUIKit.messageTransformer?.transformToGet(message)
?: message
continuation.safeResume(SceytResponse.Success(resultTransformed.toSceytUiMessage()))
}
override fun onError(error: SceytException?) {
continuation.safeResume(SceytResponse.Error(error))
SceytLog.e(TAG, "sendMessage error: ${error?.message}, messageTid: " +
"${message.tid}, body: ${message.body}")
}
})
tmpMessageCb?.invoke(tmpMessage)
}
override suspend fun deleteMessage(channelId: Long, messageId: Long, deleteType: DeleteMessageType): SceytResponse {
return suspendCancellableCoroutine { continuation ->
ChannelOperator.build(channelId).deleteMessage(messageId, deleteType, object : MessageCallback {
override fun onResult(msg: Message) {
continuation.safeResume(SceytResponse.Success(msg.toSceytUiMessage()))
}
override fun onError(ex: SceytException?) {
continuation.safeResume(SceytResponse.Error(ex))
SceytLog.e(TAG, "deleteMessage error: ${ex?.message}")
}
})
}
}
override suspend fun editMessage(channelId: Long, message: SceytMessage): SceytResponse {
return suspendCancellableCoroutine { continuation ->
ChannelOperator.build(channelId).editMessage(message.toMessage(), object : MessageCallback {
override fun onResult(result: Message) {
continuation.safeResume(SceytResponse.Success(result.toSceytUiMessage()))
}
override fun onError(ex: SceytException?) {
continuation.safeResume(SceytResponse.Error(ex))
SceytLog.e(TAG, "editMessage error: ${ex?.message}")
}
})
}
}
override suspend fun markMessageAs(channelId: Long, marker: MarkerType, vararg id: Long): SceytResponse {
return suspendCancellableCoroutine { continuation ->
val callback = object : MessageMarkCallback {
override fun onResult(result: MessageListMarker) {
continuation.safeResume(SceytResponse.Success(result))
}
override fun onError(error: SceytException?) {
continuation.safeResume(SceytResponse.Error(error))
SceytLog.e(TAG, "markAs:${marker} error: ${error?.message}")
}
}
val channelOperator = ChannelOperator.build(channelId)
when (marker) {
MarkerType.Displayed -> channelOperator.markMessagesAsDisplayed(id, callback)
MarkerType.Received -> channelOperator.markMessagesAsReceived(id, callback)
else -> channelOperator.markMessages(id, marker.value, callback)
}
}
}
override suspend fun addMessagesMarker(channelId: Long, marker: String, vararg id: Long): SceytResponse {
return suspendCancellableCoroutine { continuation ->
val channelOperator = ChannelOperator.build(channelId)
channelOperator.markMessages(id, marker, object : MessageMarkCallback {
override fun onResult(result: MessageListMarker) {
continuation.safeResume(SceytResponse.Success(result))
}
override fun onError(error: SceytException?) {
continuation.safeResume(SceytResponse.Error(error))
SceytLog.e(TAG, "addMessagesMarker: $marker error: ${error?.message}")
}
})
}
}
override suspend fun getMessageById(channelId: Long, messageId: Long): SceytResponse {
return suspendCancellableCoroutine { continuation ->
ChannelOperator.build(channelId).getMessagesById(longArrayOf(messageId), object : MessagesCallback {
override fun onResult(messages: MutableList?) {
if (!messages.isNullOrEmpty())
continuation.safeResume(SceytResponse.Success(messages.first().toSceytUiMessage()))
else continuation.safeResume(SceytResponse.Error(SceytException(0, "Message not found")))
}
override fun onError(error: SceytException?) {
continuation.safeResume(SceytResponse.Error(error))
SceytLog.e(TAG, "getMessageById error: ${error?.message}")
}
})
}
}
override suspend fun sendTyping(channelId: Long, typing: Boolean) {
if (typing)
ChannelOperator.build(channelId).startTyping()
else ChannelOperator.build(channelId).stopTyping()
}
private val messagesLoadSize get() = SceytChatUIKit.config.queryLimits.messageListQueryLimit
private fun getQuery(conversationId: Long,
replyInThread: Boolean,
limit: Int,
reversed: Boolean) = MessagesListQuery.Builder(conversationId).apply {
setIsThread(replyInThread)
setLimit(limit)
setReversed(reversed)
}.build()
private fun getQueryForSearch(conversationId: Long,
replyInThread: Boolean,
limit: Int,
searchQuery: MessageSearchQuery) = MessagesListQuery.Builder(conversationId).apply {
setIsThread(replyInThread)
setLimit(limit)
setReversed(true)
setSearchQuery(searchQuery)
}.build()
private fun getQueryByType(type: String, conversationId: Long, limit: Int) = MessagesListQueryByType.Builder(conversationId, type).apply {
limit(limit)
reversed(true)
}.build()
}