refactor: split message event handlers

This commit is contained in:
Codex
2026-05-14 18:06:54 +03:00
parent 2bf81c60d6
commit c380c1a73d
4 changed files with 251 additions and 236 deletions
@@ -0,0 +1,36 @@
package dev.meloda.fast.domain
import dev.meloda.fast.model.LongPollEvent
import dev.meloda.fast.model.LongPollParsedEvent
internal class LongPollEventDispatcher {
private val listenersMap: MutableMap<LongPollEvent, MutableList<VkEventCallback<LongPollParsedEvent>>> =
mutableMapOf()
@Suppress("UNCHECKED_CAST")
fun <T : LongPollParsedEvent> dispatch(eventType: LongPollEvent, event: T) {
listenersMap[eventType]?.forEach { callback ->
(callback as? VkEventCallback<T>)?.onEvent(event)
}
}
fun dispatchAll(eventType: LongPollEvent, events: List<LongPollParsedEvent>) {
events.forEach { event -> dispatch(eventType, event) }
}
@Suppress("UNCHECKED_CAST")
fun <T : LongPollParsedEvent> registerListener(
eventType: LongPollEvent,
listener: VkEventCallback<T>
) {
listenersMap[eventType] = (listenersMap[eventType] ?: mutableListOf())
.also { it.add(listener as VkEventCallback<LongPollParsedEvent>) }
}
fun <T : LongPollParsedEvent> registerListeners(
eventTypes: List<LongPollEvent>,
listener: VkEventCallback<T>
) {
eventTypes.forEach { eventType -> registerListener(eventType, listener) }
}
}
@@ -43,20 +43,7 @@ class LongPollUpdatesParser(
get() = Dispatchers.Default + job + exceptionHandler get() = Dispatchers.Default + job + exceptionHandler
private val coroutineScope = CoroutineScope(coroutineContext) private val coroutineScope = CoroutineScope(coroutineContext)
private val eventDispatcher = LongPollEventDispatcher()
private val listenersMap: MutableMap<LongPollEvent, MutableList<VkEventCallback<LongPollParsedEvent>>> =
mutableMapOf()
@Suppress("UNCHECKED_CAST")
private fun <T : LongPollParsedEvent> dispatch(eventType: LongPollEvent, event: T) {
listenersMap[eventType]?.forEach { callback ->
(callback as? VkEventCallback<T>)?.onEvent(event)
}
}
private fun dispatchAll(eventType: LongPollEvent, events: List<LongPollParsedEvent>) {
events.forEach { event -> dispatch(eventType, event) }
}
fun parseNextUpdate(event: List<Any>) { fun parseNextUpdate(event: List<Any>) {
val eventId = event.first().asInt() val eventId = event.first().asInt()
@@ -107,7 +94,7 @@ class LongPollUpdatesParser(
marked = true marked = true
) )
eventsToSend += eventToSend eventsToSend += eventToSend
dispatch(LongPollEvent.MARKED_AS_IMPORTANT, eventToSend) eventDispatcher.dispatch(LongPollEvent.MARKED_AS_IMPORTANT, eventToSend)
} }
MessageFlags.SPAM -> { MessageFlags.SPAM -> {
@@ -116,7 +103,7 @@ class LongPollUpdatesParser(
cmId = cmId cmId = cmId
) )
eventsToSend += eventToSend eventsToSend += eventToSend
dispatch(LongPollEvent.MARKED_AS_SPAM, eventToSend) eventDispatcher.dispatch(LongPollEvent.MARKED_AS_SPAM, eventToSend)
} }
MessageFlags.DELETED -> { MessageFlags.DELETED -> {
@@ -135,7 +122,7 @@ class LongPollUpdatesParser(
) )
} }
eventsToSend += eventToSend eventsToSend += eventToSend
dispatch(LongPollEvent.MESSAGE_DELETED, eventToSend) eventDispatcher.dispatch(LongPollEvent.MESSAGE_DELETED, eventToSend)
} }
MessageFlags.AUDIO_LISTENED -> { MessageFlags.AUDIO_LISTENED -> {
@@ -144,14 +131,14 @@ class LongPollUpdatesParser(
cmId = cmId cmId = cmId
) )
eventsToSend += eventToSend eventsToSend += eventToSend
dispatch(LongPollEvent.AUDIO_MESSAGE_LISTENED, eventToSend) eventDispatcher.dispatch(LongPollEvent.AUDIO_MESSAGE_LISTENED, eventToSend)
} }
else -> Unit else -> Unit
} }
} }
dispatchAll(LongPollEvent.MESSAGE_SET_FLAGS, eventsToSend) eventDispatcher.dispatchAll(LongPollEvent.MESSAGE_SET_FLAGS, eventsToSend)
} }
private fun parseMessageClearFlags(eventType: ApiEvent, event: List<Any>) { private fun parseMessageClearFlags(eventType: ApiEvent, event: List<Any>) {
@@ -175,7 +162,7 @@ class LongPollUpdatesParser(
marked = false marked = false
) )
eventsToSend += eventToSend eventsToSend += eventToSend
dispatch(LongPollEvent.MARKED_AS_IMPORTANT, eventToSend) eventDispatcher.dispatch(LongPollEvent.MARKED_AS_IMPORTANT, eventToSend)
} }
MessageFlags.SPAM -> { MessageFlags.SPAM -> {
@@ -189,7 +176,7 @@ class LongPollUpdatesParser(
val eventToSend = val eventToSend =
LongPollParsedEvent.MessageMarkedAsNotSpam(message = message) LongPollParsedEvent.MessageMarkedAsNotSpam(message = message)
eventsToSend += eventToSend eventsToSend += eventToSend
dispatch(LongPollEvent.MARKED_AS_NOT_SPAM, eventToSend) eventDispatcher.dispatch(LongPollEvent.MARKED_AS_NOT_SPAM, eventToSend)
} }
} }
} }
@@ -205,7 +192,7 @@ class LongPollUpdatesParser(
val eventToSend = val eventToSend =
LongPollParsedEvent.MessageRestored(message = message) LongPollParsedEvent.MessageRestored(message = message)
eventsToSend += eventToSend eventsToSend += eventToSend
dispatch(LongPollEvent.MESSAGE_RESTORED, eventToSend) eventDispatcher.dispatch(LongPollEvent.MESSAGE_RESTORED, eventToSend)
} }
} }
} }
@@ -214,7 +201,7 @@ class LongPollUpdatesParser(
} }
} }
dispatchAll(LongPollEvent.MESSAGE_CLEAR_FLAGS, eventsToSend) eventDispatcher.dispatchAll(LongPollEvent.MESSAGE_CLEAR_FLAGS, eventsToSend)
} }
} }
@@ -238,7 +225,7 @@ class LongPollUpdatesParser(
}.await() }.await()
message?.let { message?.let {
dispatch( eventDispatcher.dispatch(
LongPollEvent.MESSAGE_NEW, LongPollEvent.MESSAGE_NEW,
LongPollParsedEvent.NewMessage( LongPollParsedEvent.NewMessage(
message = message, message = message,
@@ -263,7 +250,7 @@ class LongPollUpdatesParser(
peerId = peerId, peerId = peerId,
cmId = cmId cmId = cmId
)?.let { message -> )?.let { message ->
dispatch(LongPollEvent.MESSAGE_EDITED, LongPollParsedEvent.MessageEdited(message)) eventDispatcher.dispatch(LongPollEvent.MESSAGE_EDITED, LongPollParsedEvent.MessageEdited(message))
} }
} }
} }
@@ -274,7 +261,7 @@ class LongPollUpdatesParser(
val cmId = event[2].asLong() val cmId = event[2].asLong()
val unreadCount = event[3].asInt() val unreadCount = event[3].asInt()
dispatch( eventDispatcher.dispatch(
LongPollEvent.INCOMING_MESSAGE_READ, LongPollEvent.INCOMING_MESSAGE_READ,
LongPollParsedEvent.IncomingMessageRead( LongPollParsedEvent.IncomingMessageRead(
peerId = peerId, peerId = peerId,
@@ -290,7 +277,7 @@ class LongPollUpdatesParser(
val cmId = event[2].asLong() val cmId = event[2].asLong()
val unreadCount = event[3].asInt() val unreadCount = event[3].asInt()
dispatch( eventDispatcher.dispatch(
LongPollEvent.OUTGOING_MESSAGE_READ, LongPollEvent.OUTGOING_MESSAGE_READ,
LongPollParsedEvent.OutgoingMessageRead( LongPollParsedEvent.OutgoingMessageRead(
peerId = peerId, peerId = peerId,
@@ -330,14 +317,14 @@ class LongPollUpdatesParser(
archived = false archived = false
) )
eventsToSend += eventToSend eventsToSend += eventToSend
dispatch(LongPollEvent.CHAT_ARCHIVED, eventToSend) eventDispatcher.dispatch(LongPollEvent.CHAT_ARCHIVED, eventToSend)
} }
else -> Unit else -> Unit
} }
} }
dispatchAll(LongPollEvent.CHAT_CLEAR_FLAGS, eventsToSend) eventDispatcher.dispatchAll(LongPollEvent.CHAT_CLEAR_FLAGS, eventsToSend)
} }
} }
@@ -371,14 +358,14 @@ class LongPollUpdatesParser(
archived = true archived = true
) )
eventsToSend += eventToSend eventsToSend += eventToSend
dispatch(LongPollEvent.CHAT_ARCHIVED, eventToSend) eventDispatcher.dispatch(LongPollEvent.CHAT_ARCHIVED, eventToSend)
} }
else -> Unit else -> Unit
} }
} }
dispatchAll(LongPollEvent.CHAT_SET_FLAGS, eventsToSend) eventDispatcher.dispatchAll(LongPollEvent.CHAT_SET_FLAGS, eventsToSend)
} }
} }
@@ -388,7 +375,7 @@ class LongPollUpdatesParser(
val peerId = event[1].asLong() val peerId = event[1].asLong()
val cmId = event[2].asLong() val cmId = event[2].asLong()
dispatch( eventDispatcher.dispatch(
LongPollEvent.CHAT_CLEARED, LongPollEvent.CHAT_CLEARED,
LongPollParsedEvent.ChatCleared( LongPollParsedEvent.ChatCleared(
peerId = peerId, peerId = peerId,
@@ -403,7 +390,7 @@ class LongPollUpdatesParser(
val peerId = event[1].asLong() val peerId = event[1].asLong()
val majorId = event[2].asInt() val majorId = event[2].asInt()
dispatch( eventDispatcher.dispatch(
LongPollEvent.CHAT_MAJOR_CHANGED, LongPollEvent.CHAT_MAJOR_CHANGED,
LongPollParsedEvent.ChatMajorChanged( LongPollParsedEvent.ChatMajorChanged(
peerId = peerId, peerId = peerId,
@@ -418,7 +405,7 @@ class LongPollUpdatesParser(
val peerId = event[1].asLong() val peerId = event[1].asLong()
val minorId = event[2].asInt() val minorId = event[2].asInt()
dispatch( eventDispatcher.dispatch(
LongPollEvent.CHAT_MINOR_CHANGED, LongPollEvent.CHAT_MINOR_CHANGED,
LongPollParsedEvent.ChatMinorChanged( LongPollParsedEvent.ChatMinorChanged(
peerId = peerId, peerId = peerId,
@@ -456,7 +443,7 @@ class LongPollUpdatesParser(
// if userIds contains only account's id, then we don't need to show our status // if userIds contains only account's id, then we don't need to show our status
if (userIds.isEmpty()) return if (userIds.isEmpty()) return
dispatch( eventDispatcher.dispatch(
longPollEvent, longPollEvent,
LongPollParsedEvent.Interaction( LongPollParsedEvent.Interaction(
interactionType = interactionType, interactionType = interactionType,
@@ -479,7 +466,7 @@ class LongPollUpdatesParser(
val archiveUnreadUnmutedCount = event[8].asInt() val archiveUnreadUnmutedCount = event[8].asInt()
val archiveMentionsCount = event[9].asInt() val archiveMentionsCount = event[9].asInt()
dispatch( eventDispatcher.dispatch(
LongPollEvent.UNREAD_COUNTER_UPDATE, LongPollEvent.UNREAD_COUNTER_UPDATE,
LongPollParsedEvent.UnreadCounter( LongPollParsedEvent.UnreadCounter(
unread = unreadCount, unread = unreadCount,
@@ -504,7 +491,7 @@ class LongPollUpdatesParser(
peerId = peerId, peerId = peerId,
cmId = cmId cmId = cmId
)?.let { message -> )?.let { message ->
dispatch(LongPollEvent.MESSAGE_UPDATED, LongPollParsedEvent.MessageUpdated(message)) eventDispatcher.dispatch(LongPollEvent.MESSAGE_UPDATED, LongPollParsedEvent.MessageUpdated(message))
} }
} }
} }
@@ -516,7 +503,7 @@ class LongPollUpdatesParser(
coroutineScope.launch(Dispatchers.IO) { coroutineScope.launch(Dispatchers.IO) {
loadMessage(messageId = messageId)?.let { message -> loadMessage(messageId = messageId)?.let { message ->
dispatch( eventDispatcher.dispatch(
LongPollEvent.MESSAGE_CACHE_CLEAR, LongPollEvent.MESSAGE_CACHE_CLEAR,
LongPollParsedEvent.MessageCacheClear(message) LongPollParsedEvent.MessageCacheClear(message)
) )
@@ -587,88 +574,68 @@ class LongPollUpdatesParser(
} }
} }
@Suppress("UNCHECKED_CAST")
private fun <T : LongPollParsedEvent> registerListener(
eventType: LongPollEvent,
listener: VkEventCallback<T>
) {
listenersMap.let { map ->
map[eventType] = (map[eventType] ?: mutableListOf())
.also {
it.add(listener as VkEventCallback<LongPollParsedEvent>)
}
}
}
private fun <T : LongPollParsedEvent> registerListeners(
eventTypes: List<LongPollEvent>,
listener: VkEventCallback<T>
) {
eventTypes.forEach { eventType -> registerListener(eventType, listener) }
}
fun onMessageSetFlags(block: (LongPollParsedEvent) -> Unit) { fun onMessageSetFlags(block: (LongPollParsedEvent) -> Unit) {
registerListener(LongPollEvent.MESSAGE_SET_FLAGS, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.MESSAGE_SET_FLAGS, assembleEventCallback(block))
} }
fun onMessageMarkedAsImportant(block: (LongPollParsedEvent.MessageMarkedAsImportant) -> Unit) { fun onMessageMarkedAsImportant(block: (LongPollParsedEvent.MessageMarkedAsImportant) -> Unit) {
registerListener(LongPollEvent.MARKED_AS_IMPORTANT, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.MARKED_AS_IMPORTANT, assembleEventCallback(block))
} }
fun onMessageMarkedAsSpam(block: (LongPollParsedEvent.MessageMarkedAsSpam) -> Unit) { fun onMessageMarkedAsSpam(block: (LongPollParsedEvent.MessageMarkedAsSpam) -> Unit) {
registerListener(LongPollEvent.MARKED_AS_SPAM, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.MARKED_AS_SPAM, assembleEventCallback(block))
} }
fun onMessageDeleted(block: (LongPollParsedEvent.MessageDeleted) -> Unit) { fun onMessageDeleted(block: (LongPollParsedEvent.MessageDeleted) -> Unit) {
registerListener(LongPollEvent.MESSAGE_DELETED, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.MESSAGE_DELETED, assembleEventCallback(block))
} }
fun onMessageClearFlags(block: (LongPollParsedEvent) -> Unit) { fun onMessageClearFlags(block: (LongPollParsedEvent) -> Unit) {
registerListener(LongPollEvent.MESSAGE_CLEAR_FLAGS, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.MESSAGE_CLEAR_FLAGS, assembleEventCallback(block))
} }
fun onMessageMarkedAsNotSpam(block: (LongPollParsedEvent.MessageMarkedAsNotSpam) -> Unit) { fun onMessageMarkedAsNotSpam(block: (LongPollParsedEvent.MessageMarkedAsNotSpam) -> Unit) {
registerListener(LongPollEvent.MARKED_AS_NOT_SPAM, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.MARKED_AS_NOT_SPAM, assembleEventCallback(block))
} }
fun onMessageRestored(block: (LongPollParsedEvent.MessageRestored) -> Unit) { fun onMessageRestored(block: (LongPollParsedEvent.MessageRestored) -> Unit) {
registerListener(LongPollEvent.MESSAGE_RESTORED, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.MESSAGE_RESTORED, assembleEventCallback(block))
} }
fun onNewMessage(block: (LongPollParsedEvent.NewMessage) -> Unit) { fun onNewMessage(block: (LongPollParsedEvent.NewMessage) -> Unit) {
registerListener(LongPollEvent.MESSAGE_NEW, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.MESSAGE_NEW, assembleEventCallback(block))
} }
fun onMessageEdited(block: (LongPollParsedEvent.MessageEdited) -> Unit) { fun onMessageEdited(block: (LongPollParsedEvent.MessageEdited) -> Unit) {
registerListener(LongPollEvent.MESSAGE_EDITED, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.MESSAGE_EDITED, assembleEventCallback(block))
} }
fun onMessageIncomingRead(block: (LongPollParsedEvent.IncomingMessageRead) -> Unit) { fun onMessageIncomingRead(block: (LongPollParsedEvent.IncomingMessageRead) -> Unit) {
registerListener(LongPollEvent.INCOMING_MESSAGE_READ, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.INCOMING_MESSAGE_READ, assembleEventCallback(block))
} }
fun onMessageOutgoingRead(block: (LongPollParsedEvent.OutgoingMessageRead) -> Unit) { fun onMessageOutgoingRead(block: (LongPollParsedEvent.OutgoingMessageRead) -> Unit) {
registerListener(LongPollEvent.OUTGOING_MESSAGE_READ, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.OUTGOING_MESSAGE_READ, assembleEventCallback(block))
} }
fun onChatCleared(block: (LongPollParsedEvent.ChatCleared) -> Unit) { fun onChatCleared(block: (LongPollParsedEvent.ChatCleared) -> Unit) {
registerListener(LongPollEvent.CHAT_CLEARED, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.CHAT_CLEARED, assembleEventCallback(block))
} }
fun onChatMajorChanged(block: (LongPollParsedEvent.ChatMajorChanged) -> Unit) { fun onChatMajorChanged(block: (LongPollParsedEvent.ChatMajorChanged) -> Unit) {
registerListener(LongPollEvent.CHAT_MAJOR_CHANGED, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.CHAT_MAJOR_CHANGED, assembleEventCallback(block))
} }
fun onChatMinorChanged(block: (LongPollParsedEvent.ChatMinorChanged) -> Unit) { fun onChatMinorChanged(block: (LongPollParsedEvent.ChatMinorChanged) -> Unit) {
registerListener(LongPollEvent.CHAT_MINOR_CHANGED, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.CHAT_MINOR_CHANGED, assembleEventCallback(block))
} }
fun onChatArchived(block: (LongPollParsedEvent.ChatArchived) -> Unit) { fun onChatArchived(block: (LongPollParsedEvent.ChatArchived) -> Unit) {
registerListener(LongPollEvent.CHAT_ARCHIVED, assembleEventCallback(block)) eventDispatcher.registerListener(LongPollEvent.CHAT_ARCHIVED, assembleEventCallback(block))
} }
fun onInteractions(block: (LongPollParsedEvent.Interaction) -> Unit) { fun onInteractions(block: (LongPollParsedEvent.Interaction) -> Unit) {
registerListeners( eventDispatcher.registerListeners(
eventTypes = listOf( eventTypes = listOf(
LongPollEvent.TYPING, LongPollEvent.TYPING,
LongPollEvent.AUDIO_MESSAGE_RECORDING, LongPollEvent.AUDIO_MESSAGE_RECORDING,
@@ -0,0 +1,158 @@
package dev.meloda.fast.messageshistory
import android.util.Log
import com.conena.nanokt.collections.indexOfFirstOrNull
import dev.meloda.fast.common.extensions.setValue
import dev.meloda.fast.messageshistory.model.MessagesHistoryScreenState
import dev.meloda.fast.model.LongPollParsedEvent
import dev.meloda.fast.model.api.domain.VkMessage
import kotlinx.coroutines.flow.MutableStateFlow
import kotlin.math.abs
internal class MessagesHistoryLongPollEventHandler(
private val screenState: MutableStateFlow<MessagesHistoryScreenState>,
private val messages: MutableStateFlow<List<VkMessage>>,
private val syncUiMessages: () -> Unit
) {
fun onNewMessage(event: LongPollParsedEvent.NewMessage) {
val message = event.message
Log.d("MessagesHistoryViewModel", "handleNewMessage: $message")
if (message.peerId != screenState.value.convoId) return
if (messages.value.indexOfFirstOrNull { it.id == message.id } != null) return
val randomIds = messages.value.map(VkMessage::randomId)
if (message.randomId != 0L && message.randomId in randomIds) return
val newMessages = messages.value.toMutableList()
newMessages.add(0, message)
messages.setValue { newMessages }
syncUiMessages()
}
fun onMessageEdited(event: LongPollParsedEvent.MessageEdited) {
val message = event.message
if (message.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val index = newMessages.indexOfFirstOrNull { it.id == message.id }
if (index == null) {
return
}
newMessages[index] = message
messages.setValue { newMessages }
syncUiMessages()
}
fun onReadIncoming(event: LongPollParsedEvent.IncomingMessageRead) {
if (event.peerId != screenState.value.convoId) return
val index = messages.value.indexOfFirstOrNull { it.cmId == event.cmId }
if (index == null) return
val newConvo = screenState.value.convo.copy(
inReadCmId = event.cmId
)
screenState.setValue { old ->
old.copy(convo = newConvo)
}
syncUiMessages()
}
fun onReadOutgoing(event: LongPollParsedEvent.OutgoingMessageRead) {
if (event.peerId != screenState.value.convoId) return
val index = messages.value.indexOfFirstOrNull { it.cmId == event.cmId }
if (index == null) return
val newConvo = screenState.value.convo.copy(
outReadCmId = event.cmId
)
screenState.setValue { old ->
old.copy(convo = newConvo)
}
syncUiMessages()
}
fun onMessageDeleted(event: LongPollParsedEvent.MessageDeleted) {
if (event.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val index = newMessages.indexOfFirstOrNull { it.cmId == event.cmId }
if (index == null) return
newMessages.removeAt(index)
messages.setValue { newMessages }
syncUiMessages()
}
fun onMessageRestored(event: LongPollParsedEvent.MessageRestored) {
if (event.message.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val minDate = newMessages.minOf(VkMessage::date)
if (event.message.date < minDate) return
newMessages.add(event.message)
messages.setValue { newMessages.sorted() }
syncUiMessages()
}
fun onMessageMarkedAsImportant(event: LongPollParsedEvent.MessageMarkedAsImportant) {
if (event.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val index = newMessages.indexOfFirstOrNull { it.cmId == event.cmId }
if (index == null) return
newMessages[index] = newMessages[index].copy(isImportant = event.marked)
messages.setValue { newMessages }
syncUiMessages()
}
fun onMessageMarkedAsSpam(event: LongPollParsedEvent.MessageMarkedAsSpam) {
if (event.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val index = newMessages.indexOfFirstOrNull { it.cmId == event.cmId }
if (index == null) return
newMessages.removeAt(index)
messages.setValue { newMessages }
syncUiMessages()
}
fun onMessageMarkedAsNotSpam(event: LongPollParsedEvent.MessageMarkedAsNotSpam) {
if (event.message.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val maxDate = newMessages.maxOf(VkMessage::date)
val minDate = newMessages.minOf(VkMessage::date)
if (event.message.date !in minDate..maxDate) return
newMessages.add(event.message)
messages.setValue { newMessages.sorted() }
syncUiMessages()
}
private fun List<VkMessage>.sorted(): List<VkMessage> {
return sortedWith { m1, m2 ->
val dateDiff = m2.date - m1.date
if (dateDiff != 0) {
dateDiff
} else {
val idDiff = m2.id - m1.id
idDiff.toInt()
}
}
}
}
@@ -58,7 +58,6 @@ import dev.meloda.fast.messageshistory.model.MessageOption
import dev.meloda.fast.messageshistory.model.MessagesHistoryScreenState import dev.meloda.fast.messageshistory.model.MessagesHistoryScreenState
import dev.meloda.fast.messageshistory.navigation.MessagesHistory import dev.meloda.fast.messageshistory.navigation.MessagesHistory
import dev.meloda.fast.model.BaseError import dev.meloda.fast.model.BaseError
import dev.meloda.fast.model.LongPollParsedEvent
import dev.meloda.fast.model.api.domain.FormatDataType import dev.meloda.fast.model.api.domain.FormatDataType
import dev.meloda.fast.model.api.domain.VkMessage import dev.meloda.fast.model.api.domain.VkMessage
import dev.meloda.fast.model.api.domain.VkPhotoDomain import dev.meloda.fast.model.api.domain.VkPhotoDomain
@@ -120,6 +119,13 @@ class MessagesHistoryViewModelImpl(
private var editMessage: VkMessage? = null private var editMessage: VkMessage? = null
private val longPollEventHandler = MessagesHistoryLongPollEventHandler(
screenState = screenState,
messages = messages
) {
syncUiMessages()
}
init { init {
val arguments = MessagesHistory.from(savedStateHandle).arguments val arguments = MessagesHistory.from(savedStateHandle).arguments
@@ -128,15 +134,15 @@ class MessagesHistoryViewModelImpl(
loadConvo() loadConvo()
loadMessagesHistory() loadMessagesHistory()
updatesParser.onNewMessage(::handleNewMessage) updatesParser.onNewMessage(longPollEventHandler::onNewMessage)
updatesParser.onMessageEdited(::handleEditedMessage) updatesParser.onMessageEdited(longPollEventHandler::onMessageEdited)
updatesParser.onMessageIncomingRead(::handleReadIncomingEvent) updatesParser.onMessageIncomingRead(longPollEventHandler::onReadIncoming)
updatesParser.onMessageOutgoingRead(::handleReadOutgoingEvent) updatesParser.onMessageOutgoingRead(longPollEventHandler::onReadOutgoing)
updatesParser.onMessageDeleted(::handleMessageDeleted) updatesParser.onMessageDeleted(longPollEventHandler::onMessageDeleted)
updatesParser.onMessageRestored(::handleMessageRestored) updatesParser.onMessageRestored(longPollEventHandler::onMessageRestored)
updatesParser.onMessageMarkedAsImportant(::handleMessageMarkedAsImportant) updatesParser.onMessageMarkedAsImportant(longPollEventHandler::onMessageMarkedAsImportant)
updatesParser.onMessageMarkedAsSpam(::handleMessageMarkedAsSpam) updatesParser.onMessageMarkedAsSpam(longPollEventHandler::onMessageMarkedAsSpam)
updatesParser.onMessageMarkedAsNotSpam(::handleMessageMarkedAsNotSpam) updatesParser.onMessageMarkedAsNotSpam(longPollEventHandler::onMessageMarkedAsNotSpam)
} }
override fun onNavigationConsumed() { override fun onNavigationConsumed() {
@@ -685,158 +691,6 @@ class MessagesHistoryViewModelImpl(
} }
} }
private fun handleNewMessage(event: LongPollParsedEvent.NewMessage) {
val message = event.message
Log.d("MessagesHistoryViewModel", "handleNewMessage: $message")
if (message.peerId != screenState.value.convoId) return
if (messages.value.indexOfFirstOrNull { it.id == message.id } != null) return
val randomIds = messages.value.map(VkMessage::randomId)
if (message.randomId != 0L && message.randomId in randomIds) return
val newMessages = messages.value.toMutableList()
newMessages.add(0, message)
messages.setValue { newMessages }
syncUiMessages()
}
private fun handleEditedMessage(event: LongPollParsedEvent.MessageEdited) {
val message = event.message
if (message.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val index = newMessages.indexOfFirstOrNull { it.id == message.id }
if (index == null) { // сообщения нет в списке
// pizdets
} else {
newMessages[index] = message
messages.setValue { newMessages }
syncUiMessages()
}
}
private fun handleReadIncomingEvent(event: LongPollParsedEvent.IncomingMessageRead) {
if (event.peerId != screenState.value.convoId) return
val messages = messages.value
val index = messages.indexOfFirstOrNull { it.cmId == event.cmId }
if (index == null) { // диалога нет в списке
// pizdets
} else {
val newConvo = screenState.value.convo.copy(
inReadCmId = event.cmId
)
screenState.setValue { old ->
old.copy(convo = newConvo)
}
syncUiMessages()
}
}
private fun handleReadOutgoingEvent(event: LongPollParsedEvent.OutgoingMessageRead) {
if (event.peerId != screenState.value.convoId) return
val messages = messages.value
val index = messages.indexOfFirstOrNull { it.cmId == event.cmId }
if (index == null) { // сообщения нет в списке
// pizdets
} else {
val newConvo = screenState.value.convo.copy(
outReadCmId = event.cmId
)
screenState.setValue { old ->
old.copy(convo = newConvo)
}
syncUiMessages()
}
}
private fun handleMessageDeleted(event: LongPollParsedEvent.MessageDeleted) {
if (event.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val index = newMessages.indexOfFirstOrNull { it.cmId == event.cmId }
if (index == null) { // сообщения нет в списке
// pizdets
} else {
newMessages.removeAt(index)
messages.setValue { newMessages }
syncUiMessages()
}
}
private fun handleMessageRestored(event: LongPollParsedEvent.MessageRestored) {
if (event.message.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val minDate = newMessages.minOf(VkMessage::date)
if (event.message.date < minDate) { // сообщения не должно быть в списке
// pizdets
return
}
newMessages.add(event.message)
messages.setValue { newMessages.sorted() }
syncUiMessages()
}
private fun handleMessageMarkedAsImportant(event: LongPollParsedEvent.MessageMarkedAsImportant) {
if (event.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val index = newMessages.indexOfFirstOrNull { it.cmId == event.cmId }
if (index == null) { // сообщения нет в списке
// pizdets
} else {
val newMessage = newMessages[index].copy(isImportant = event.marked)
newMessages[index] = newMessage
messages.setValue { newMessages }
syncUiMessages()
}
}
private fun handleMessageMarkedAsSpam(event: LongPollParsedEvent.MessageMarkedAsSpam) {
if (event.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val index = newMessages.indexOfFirstOrNull { it.cmId == event.cmId }
if (index == null) { // сообщения нет в списке
// pizdets
} else {
newMessages.removeAt(index)
messages.setValue { newMessages }
syncUiMessages()
}
}
private fun handleMessageMarkedAsNotSpam(event: LongPollParsedEvent.MessageMarkedAsNotSpam) {
if (event.message.peerId != screenState.value.convoId) return
val newMessages = messages.value.toMutableList()
val maxDate = newMessages.maxOf(VkMessage::date)
val minDate = newMessages.minOf(VkMessage::date)
if (event.message.date !in minDate..maxDate) return
newMessages.add(event.message)
messages.setValue { newMessages.sorted() }
syncUiMessages()
}
private fun loadConvo() { private fun loadConvo() {
Log.d("MessagesHistoryViewModelImpl", "loadConvo()") Log.d("MessagesHistoryViewModelImpl", "loadConvo()")