All Downloads are FREE. Search and download functionalities are using the official Maven repository.
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.services.SceytSyncManager.kt Maven / Gradle / Ivy
package com.sceyt.chatuikit.services
import com.sceyt.chatuikit.SceytChatUIKit
import com.sceyt.chatuikit.data.models.SceytResponse
import com.sceyt.chatuikit.data.models.channels.GetAllChannelsResponse
import com.sceyt.chatuikit.data.models.channels.SceytChannel
import com.sceyt.chatuikit.data.models.messages.SceytMessage
import com.sceyt.chatuikit.extensions.TAG
import com.sceyt.chatuikit.koin.SceytKoinComponent
import com.sceyt.chatuikit.logger.SceytLog
import com.sceyt.chatuikit.persistence.extensions.asLiveData
import com.sceyt.chatuikit.persistence.interactor.ChannelInteractor
import com.sceyt.chatuikit.persistence.interactor.MessageInteractor
import com.sceyt.chatuikit.persistence.logicimpl.channel.ChannelsCache
import com.sceyt.chatuikit.persistence.shared.LiveEvent
import com.sceyt.chatuikit.presentation.common.ConcurrentHashSet
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.resume
class SceytSyncManager(private val channelInteractor: ChannelInteractor,
private val messageInteractor: MessageInteractor,
private val channelsCache: ChannelsCache) : SceytKoinComponent {
private var syncResultData: SyncResultData = SyncResultData()
@Volatile
private var syncIsInProcess: Boolean = false
private val syncResultCallbacks = ConcurrentHashSet<(Result) -> Unit>()
private var syncContext: CoroutineContext? = null
companion object {
private val syncChannelsFinished_ = LiveEvent()
val syncChannelsFinished = syncChannelsFinished_.asLiveData()
private val syncChannelMessagesFinished_ = LiveEvent>>()
val syncChannelMessagesFinished = syncChannelMessagesFinished_.asLiveData()
}
suspend fun startSync(force: Boolean, resultCallback: ((Result) -> Unit)? = null) {
resultCallback?.let { syncResultCallbacks.add(it) }
if (syncIsInProcess)
return
if (!channelsCache.initialized && !force) {
val errorMessage = "Ui kit still not have loaded channels to sync, no need to sync"
finishSyncWithError(Exception(errorMessage))
SceytLog.e(TAG, errorMessage)
return
}
val coroutineContext = getCoroutineContext().also { syncContext = it }
withContext(coroutineContext) {
syncIsInProcess = true
syncResultData = SyncResultData()
val result = getChannels()
withContext(Dispatchers.Main) {
syncResultCallbacks.forEach {
it(Result.success(result))
}
}
syncResultCallbacks.clear()
syncIsInProcess = false
}
}
suspend fun syncConversationMessagesAfter(channelId: Long, fromMessageId: Long) {
val response = channelInteractor.getChannelFromServer(channelId)
if (response is SceytResponse.Success && response.data != null)
syncMessagesAfter(response.data, fromMessageId, true)
}
fun cancelSync() {
syncContext?.cancel()
syncResultCallbacks.clear()
}
private suspend fun getChannels(): SyncResultData {
return coroutineScope {
suspendCancellableCoroutine { cont ->
launch(Dispatchers.IO) {
val syncChannelData = SyncChannelData(mutableSetOf(), false)
val limit = SceytChatUIKit.config.queryLimits.channelListQueryLimit
channelInteractor.syncChannels(limit).collect {
when (it) {
is GetAllChannelsResponse.Error -> {
syncChannelData.withError = true
cont.resume(syncResultData)
}
is GetAllChannelsResponse.Proportion -> {
val channels = it.channels
syncChannelsMessages(channels)
syncChannelData.channels.addAll(channels)
}
GetAllChannelsResponse.SuccessfullyFinished -> {
syncChannelsFinished_.postValue(syncChannelData)
cont.resume(syncResultData)
}
}
}
}
}
}
}
private suspend fun syncChannelsMessages(list: List) {
list.forEach {
if (it.newMessageCount > 0) {
syncResultData.apply {
if (it.muted) {
unreadMessagesImMutedChannelCount += it.newMessageCount.toInt()
unreadMutedChannelsCount++
}
totalUnreadMessagesCount += it.newMessageCount.toInt()
totalUnreadChannelsCount++
}
}
loadMessages(it)
}
syncResultData.syncedChannelsCount += list.size
}
private suspend fun loadMessages(channel: SceytChannel) {
if (channel.lastMessage == null || channel.lastDisplayedMessageId == channel.lastMessage.id)
return
syncMessagesAfter(channel, channel.lastDisplayedMessageId, false)
}
private suspend fun syncMessagesAfter(channel: SceytChannel, fromMessageId: Long, syncConversation: Boolean) {
messageInteractor.syncMessagesAfterMessageId(channel.id, false, fromMessageId).collect {
if (it is SceytResponse.Success)
it.data?.let { messages ->
if (syncConversation)
syncChannelMessagesFinished_.postValue(Pair(channel, messages))
syncResultData.syncedMessagesCount += messages.size
}
}
}
private fun finishSyncWithError(exception: java.lang.Exception) {
syncResultCallbacks.forEach {
it(Result.failure(exception))
}
syncResultCallbacks.clear()
}
private fun getCoroutineContext(): CoroutineContext {
return Dispatchers.IO + Job()
}
data class SyncChannelData(
val channels: MutableSet,
var withError: Boolean
)
/**@param totalUnreadChannelsCount is total unread channels count, include muted channels.
* @param totalUnreadMessagesCount is total unread messages count, include messages in muted channels.
* @param unreadMutedChannelsCount is total unread muted channels count.
* @param unreadMessagesImMutedChannelCount is total unread messages in muted channels count.
* @param syncedChannelsCount is total synced channels count, include muted channels.
* @param syncedMessagesCount is total synced messages count, include messages in muted channels.*/
data class SyncResultData(
var totalUnreadChannelsCount: Int = 0,
var totalUnreadMessagesCount: Int = 0,
var unreadMutedChannelsCount: Int = 0,
var unreadMessagesImMutedChannelCount: Int = 0,
var syncedChannelsCount: Int = 0,
var syncedMessagesCount: Int = 0,
) {
override fun toString(): String {
return "unreadChannelsCount-> $totalUnreadChannelsCount, unreadMutedChannelsCount-> $unreadMutedChannelsCount " +
"unreadMessagesCount-> $totalUnreadMessagesCount, unreadMessagesImMutedChannelCount $unreadMessagesImMutedChannelCount" +
"syncedChannelsCount-> $syncedChannelsCount, syncedMessagesCount-> $syncedMessagesCount"
}
}
}