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.persistence.logicimpl.channel.ChannelsCache.kt Maven / Gradle / Ivy
package com.sceyt.chatuikit.persistence.logicimpl.channel
import com.sceyt.chatuikit.data.models.channels.DraftMessage
import com.sceyt.chatuikit.data.models.channels.SceytChannel
import com.sceyt.chatuikit.data.models.messages.SceytMessage
import com.sceyt.chatuikit.data.models.messages.SceytUser
import com.sceyt.chatuikit.persistence.differs.ChannelDiff
import com.sceyt.chatuikit.persistence.differs.diff
import com.sceyt.chatuikit.presentation.components.channel_list.channels.adapter.ChannelsComparatorDescBy
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
class ChannelsCache {
private var cachedData = hashMapOf()
private var pendingChannelsData = hashMapOf()
internal val initialized: Boolean
get() = cachedData.isNotEmpty()
/** fromPendingToRealChannelsData is used to store created pending channel ids and their real channel ids,
* to escape creating channel every time when sending message*/
private var fromPendingToRealChannelsData = hashMapOf()
private val lock = Any()
companion object {
private val channelUpdatedFlow_ = MutableSharedFlow(
extraBufferCapacity = 5,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val channelUpdatedFlow: SharedFlow = channelUpdatedFlow_
private val channelReactionMsgLoadedFlow_ = MutableSharedFlow(
extraBufferCapacity = 5,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val channelReactionMsgLoadedFlow: SharedFlow = channelReactionMsgLoadedFlow_
private val channelDeletedFlow_ = MutableSharedFlow(
extraBufferCapacity = 5,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val channelDeletedFlow: SharedFlow = channelDeletedFlow_
private val channelAddedFlow_ = MutableSharedFlow(
extraBufferCapacity = 5,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val channelAddedFlow: SharedFlow = channelAddedFlow_
private val pendingChannelCreatedFlow_ = MutableSharedFlow>(
extraBufferCapacity = 5,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val pendingChannelCreatedFlow: SharedFlow> = pendingChannelCreatedFlow_
private val channelDraftMessageChangesFlow_ = MutableSharedFlow(
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
val channelDraftMessageChangesFlow: SharedFlow = channelDraftMessageChangesFlow_
var currentChannelId: Long? = null
}
/** Added channels like upsert, and check is differences between channels*/
fun addAll(list: List, checkDifference: Boolean): Boolean {
synchronized(lock) {
return if (checkDifference)
putAndCheckHasDiff(list)
else {
putToCache(*list.toTypedArray())
false
}
}
}
fun add(channel: SceytChannel) {
synchronized(lock) {
if (putAndCheckHasDiff(channel).hasDifference())
channelAdded(channel)
}
}
fun addPendingChannel(channel: SceytChannel) {
synchronized(lock) {
pendingChannelsData[channel.id] = channel
if (channel.lastMessage != null)
channelAdded(channel)
}
}
fun clear() {
synchronized(lock) {
cachedData.clear()
}
}
fun getSorted(): List {
synchronized(lock) {
return cachedData.values.sortedWith(ChannelsComparatorDescBy())
}
}
fun getData(): List {
synchronized(lock) {
return cachedData.values.toList()
}
}
fun get(channelId: Long): SceytChannel? {
synchronized(lock) {
return cachedData[channelId] ?: pendingChannelsData[channelId]
}
}
fun getRealChannelIdWithPendingChannelId(pendingChannelId: Long): Long? {
synchronized(lock) {
return fromPendingToRealChannelsData[pendingChannelId]
}
}
fun upsertChannel(vararg channels: SceytChannel) {
synchronized(lock) {
channels.forEach {
val cachedChannel = cachedData[it.id] ?: pendingChannelsData[it.id]
if (cachedChannel == null) {
if (!it.pending) {
cachedData[it.id] = it
channelAdded(it)
}
} else {
val oldMsg = cachedChannel.lastMessage
val diff = putAndCheckHasDiff(it)
if (diff.hasDifference()) {
val needSort = checkNeedSortByLastMessage(oldMsg, it.lastMessage) || diff.pinStateChanged
channelUpdated(it, needSort, ChannelUpdatedType.Updated)
}
}
}
}
}
fun updateLastMessage(channelId: Long, message: SceytMessage?) {
synchronized(lock) {
cachedData[channelId]?.let { channel ->
val needSort = checkNeedSortByLastMessage(channel.lastMessage, message)
val updatedChannel = channel.copy(lastMessage = message)
cachedData[channelId] = updatedChannel
channelUpdated(updatedChannel, needSort, ChannelUpdatedType.LastMessage)
}
}
}
fun updateLastMessageWithLastRead(channelId: Long, message: SceytMessage) {
synchronized(lock) {
cachedData[channelId]?.let { channel ->
val needSort = checkNeedSortByLastMessage(channel.lastMessage, message)
val updatedChannel = channel.copy(
lastMessage = message,
lastDisplayedMessageId = message.id)
cachedData[channelId] = updatedChannel
channelUpdated(updatedChannel, needSort, ChannelUpdatedType.LastMessage)
}
}
}
fun clearedHistory(channelId: Long) {
synchronized(lock) {
cachedData[channelId]?.let { channel ->
val updatedChannel = channel.copy(
lastMessage = null,
newMessageCount = 0,
newMentionCount = 0,
newReactedMessageCount = 0,
newReactions = null,
pendingReactions = null
)
cachedData[channelId] = updatedChannel
channelUpdated(updatedChannel, true, ChannelUpdatedType.ClearedHistory)
}
}
}
fun updateMuteState(channelId: Long, muted: Boolean, muteUntil: Long = 0) {
synchronized(lock) {
cachedData[channelId]?.let { channel ->
val updatedChannel = channel.copy(
muted = muted,
mutedTill = if (muted) muteUntil else 0
)
cachedData[channelId] = updatedChannel
channelUpdated(updatedChannel, false, ChannelUpdatedType.MuteState)
}
}
}
fun updateAutoDeleteState(channelId: Long, period: Long) {
synchronized(lock) {
cachedData[channelId]?.let { channel ->
val updatedChannel = channel.copy(
messageRetentionPeriod = period
)
cachedData[channelId] = updatedChannel
channelUpdated(updatedChannel, false, ChannelUpdatedType.AutoDeleteState)
}
}
}
fun updatePinState(channelId: Long, pinnedAt: Long?) {
synchronized(lock) {
cachedData[channelId]?.let { channel ->
val updatedChannel = channel.copy(pinnedAt = pinnedAt)
cachedData[channelId] = updatedChannel
channelUpdated(updatedChannel, true, ChannelUpdatedType.PinnedAt)
}
}
}
fun updateUnreadCount(channelId: Long, count: Int) {
synchronized(lock) {
cachedData[channelId]?.let { channel ->
val updatedChannel = channel.copy(newMessageCount = count.toLong(), unread = false)
cachedData[channelId] = updatedChannel
channelUpdated(updatedChannel, false, ChannelUpdatedType.UnreadCount)
}
}
}
fun deleteChannel(id: Long) {
synchronized(lock) {
cachedData.remove(id)
channelDeletedFlow_.tryEmit(id)
}
}
fun removeFromPendingToRealChannelsData(pendingChannelId: Long) {
synchronized(lock) {
fromPendingToRealChannelsData.remove(pendingChannelId)
}
}
fun pendingChannelCreated(pendingChannelId: Long, newChannel: SceytChannel) {
synchronized(lock) {
// Removing pending channel
pendingChannelsData.remove(pendingChannelId)
// Adding already created channel to cache
cachedData[newChannel.id] = newChannel
// Adding pending channel id with real channel id for future getting real channel id by pending channel id
fromPendingToRealChannelsData[pendingChannelId] = newChannel.id
// Emitting to flow
pendingChannelCreatedFlow_.tryEmit(Pair(pendingChannelId, newChannel))
}
}
private fun channelUpdated(channel: SceytChannel, needSort: Boolean, type: ChannelUpdatedType) {
channelUpdatedFlow_.tryEmit(ChannelUpdateData(channel, needSort, type))
}
private fun channelAdded(channel: SceytChannel) {
channelAddedFlow_.tryEmit(channel)
}
fun updateMembersCount(channel: SceytChannel) {
var found = false
synchronized(lock) {
cachedData[channel.id]?.let {
val updatedChannel = it.copy(memberCount = channel.memberCount)
cachedData[channel.id] = updatedChannel
channelUpdated(updatedChannel, false, ChannelUpdatedType.Members)
found = true
}
}
if (!found)
upsertChannel(channel)
}
fun updateChannelDraftMessage(channelId: Long, draftMessage: DraftMessage?) {
synchronized(lock) {
cachedData[channelId]?.let {
val updatedChannel = it.copy(draftMessage = draftMessage)
cachedData[channelId] = updatedChannel
channelDraftMessageChangesFlow_.tryEmit(updatedChannel)
}
}
}
fun updateChannelPeer(id: Long, user: SceytUser) {
synchronized(lock) {
cachedData[id]?.let { channel ->
val updatedChannel = channel.copy(
members = channel.members?.map { member ->
if (member.user.id == user.id)
member.copy(user = user.copy())
else member
}
)
channelUpdated(updatedChannel, false, ChannelUpdatedType.Presence)
}
}
}
fun removeChannelMessageReactions(channelId: Long, messageId: Long) {
synchronized(lock) {
cachedData[channelId]?.let { channel ->
val filteredReactions = channel.newReactions?.filter { it.messageId != messageId }
val updatedChannel = channel.copy(newReactions = filteredReactions)
cachedData[channelId] = updatedChannel
}
}
}
fun channelLastReactionLoaded(channelId: Long) {
synchronized(lock) {
cachedData[channelId]?.let { channel ->
channelReactionMsgLoadedFlow_.tryEmit(channel)
}
}
}
fun onChannelMarkedAsReadOrUnread(channel: SceytChannel) {
synchronized(lock) {
cachedData[channel.id]?.let {
val updatedChannel = it.copy(
unread = channel.unread,
newMessageCount = channel.newMessageCount
)
cachedData[channel.id] = updatedChannel
channelUpdated(updatedChannel, false, ChannelUpdatedType.Updated)
}
}
}
private fun putAndCheckHasDiff(list: List): Boolean {
var detectedDiff = false
list.forEach {
if (!detectedDiff) {
val old = cachedData[it.id]
detectedDiff = old?.diff(it)?.hasDifference() ?: true
}
putToCache(it)
}
return detectedDiff
}
private fun putAndCheckHasDiff(channel: SceytChannel): ChannelDiff {
val old = cachedData[channel.id]
cachedData[channel.id] = channel
return old?.diff(channel) ?: ChannelDiff.DEFAULT
}
private fun putToCache(vararg channel: SceytChannel) {
channel.groupBy { it.pending }.forEach { group ->
if (group.key)
pendingChannelsData.putAll(group.value.associateBy { channel -> channel.id })
else
cachedData.putAll(group.value.associateBy { channel -> channel.id })
}
}
private fun checkNeedSortByLastMessage(oldMsg: SceytMessage?, newMsg: SceytMessage?): Boolean {
return oldMsg?.id != newMsg?.id || oldMsg?.createdAt != newMsg?.createdAt
}
}