forked from melod1n/fast-messenger
@@ -16,11 +16,11 @@ import dev.meloda.fast.common.LongPollController
|
||||
import dev.meloda.fast.common.VkConstants
|
||||
import dev.meloda.fast.common.extensions.listenValue
|
||||
import dev.meloda.fast.common.model.LongPollState
|
||||
import dev.meloda.fast.domain.LongPollUpdatesParser
|
||||
import dev.meloda.fast.domain.LongPollUseCase
|
||||
import dev.meloda.fast.data.UserConfig
|
||||
import dev.meloda.fast.data.processState
|
||||
import dev.meloda.fast.datastore.AppSettings
|
||||
import dev.meloda.fast.domain.LongPollUpdatesParser
|
||||
import dev.meloda.fast.domain.LongPollUseCase
|
||||
import dev.meloda.fast.model.api.data.LongPollUpdates
|
||||
import dev.meloda.fast.model.api.data.VkLongPollData
|
||||
import dev.meloda.fast.ui.R
|
||||
@@ -249,6 +249,7 @@ class LongPollingService : Service() {
|
||||
override fun onDestroy() {
|
||||
Log.d(STATE_TAG, "onDestroy")
|
||||
longPollController.updateCurrentState(LongPollState.Stopped)
|
||||
updatesParser.clearListeners()
|
||||
try {
|
||||
AppSettings.edit { putBoolean(KEY_LONG_POLL_WAS_DESTROYED, true) }
|
||||
job.cancel()
|
||||
@@ -259,8 +260,7 @@ class LongPollingService : Service() {
|
||||
}
|
||||
|
||||
override fun onTrimMemory(level: Int) {
|
||||
Log.d(STATE_TAG, "onTrimMemory")
|
||||
longPollController.updateCurrentState(LongPollState.Stopped)
|
||||
Log.d(STATE_TAG, "onTrimMemory. Level: $level")
|
||||
super.onTrimMemory(level)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,11 +11,15 @@ import dev.meloda.fast.data.processState
|
||||
import dev.meloda.fast.model.ApiEvent
|
||||
import dev.meloda.fast.model.InteractionType
|
||||
import dev.meloda.fast.model.LongPollEvent
|
||||
import dev.meloda.fast.model.LongPollParsedEvent
|
||||
import dev.meloda.fast.model.MessageFlags
|
||||
import dev.meloda.fast.model.api.domain.VkMessage
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
@@ -25,8 +29,9 @@ class LongPollUpdatesParser(
|
||||
) {
|
||||
private val job = SupervisorJob()
|
||||
|
||||
private val exceptionHandler = CoroutineExceptionHandler { _, throwable ->
|
||||
Log.d("LongPollUpdatesParser", "error: $throwable")
|
||||
private val exceptionHandler =
|
||||
CoroutineExceptionHandler { _, throwable ->
|
||||
Log.e("LongPollUpdatesParser", "error: $throwable")
|
||||
throwable.printStackTrace()
|
||||
}
|
||||
|
||||
@@ -35,7 +40,7 @@ class LongPollUpdatesParser(
|
||||
|
||||
private val coroutineScope = CoroutineScope(coroutineContext)
|
||||
|
||||
private val listenersMap: MutableMap<ApiEvent, MutableCollection<VkEventCallback<*>>> =
|
||||
private val listenersMap: MutableMap<LongPollEvent, MutableList<VkEventCallback<LongPollParsedEvent>>> =
|
||||
mutableMapOf()
|
||||
|
||||
fun parseNextUpdate(event: List<Any>) {
|
||||
@@ -50,8 +55,11 @@ class LongPollUpdatesParser(
|
||||
ApiEvent.MESSAGE_EDIT -> parseMessageEdit(eventType, event)
|
||||
ApiEvent.MESSAGE_READ_INCOMING -> parseMessageReadIncoming(eventType, event)
|
||||
ApiEvent.MESSAGE_READ_OUTGOING -> parseMessageReadOutgoing(eventType, event)
|
||||
ApiEvent.CHAT_CLEAR_FLAGS -> parseChatClearFlags(eventType, event)
|
||||
ApiEvent.CHAT_SET_FLAGS -> parseChatSetFlags(eventType, event)
|
||||
ApiEvent.MESSAGES_DELETED -> parseMessagesDeleted(eventType, event)
|
||||
ApiEvent.PIN_UNPIN_CONVERSATION -> parseConversationPinStateChanged(eventType, event)
|
||||
ApiEvent.CHAT_MAJOR_CHANGED -> parseChatMajorChanged(eventType, event)
|
||||
ApiEvent.CHAT_MINOR_CHANGED -> parseChatMinorChanged(eventType, event)
|
||||
|
||||
ApiEvent.TYPING,
|
||||
ApiEvent.AUDIO_MESSAGE_RECORDING,
|
||||
@@ -59,14 +67,10 @@ class LongPollUpdatesParser(
|
||||
ApiEvent.VIDEO_UPLOADING,
|
||||
ApiEvent.FILE_UPLOADING -> parseInteraction(eventType, event)
|
||||
|
||||
ApiEvent.UNREAD_COUNT_UPDATE -> onNewEvent(eventType, event)
|
||||
ApiEvent.UNREAD_COUNT_UPDATE -> parseUnreadCounterUpdate(eventType, event)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onNewEvent(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "newEvent: $eventType: $event")
|
||||
}
|
||||
|
||||
private fun parseInteraction(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
|
||||
@@ -79,6 +83,15 @@ class LongPollUpdatesParser(
|
||||
else -> return
|
||||
}
|
||||
|
||||
val longPollEvent: LongPollEvent = when (eventType) {
|
||||
ApiEvent.TYPING -> LongPollEvent.TYPING
|
||||
ApiEvent.AUDIO_MESSAGE_RECORDING -> LongPollEvent.AUDIO_MESSAGE_RECORDING
|
||||
ApiEvent.PHOTO_UPLOADING -> LongPollEvent.PHOTO_UPLOADING
|
||||
ApiEvent.VIDEO_UPLOADING -> LongPollEvent.VIDEO_UPLOADING
|
||||
ApiEvent.FILE_UPLOADING -> LongPollEvent.FILE_UPLOADING
|
||||
else -> return
|
||||
}
|
||||
|
||||
val peerId = event[1].asInt()
|
||||
val userIds = event[2].toList(Any::asInt).filter { it != UserConfig.userId }
|
||||
val totalCount = event[3].asInt()
|
||||
@@ -87,12 +100,11 @@ class LongPollUpdatesParser(
|
||||
// if userIds contains only account's id, then we don't need to show our status
|
||||
if (userIds.isEmpty()) return
|
||||
|
||||
coroutineScope.launch {
|
||||
listenersMap[eventType]?.let { listeners ->
|
||||
listenersMap[longPollEvent]?.let { listeners ->
|
||||
listeners.forEach { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.Interaction>)
|
||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.Interaction>)
|
||||
.onEvent(
|
||||
LongPollEvent.Interaction(
|
||||
LongPollParsedEvent.Interaction(
|
||||
interactionType = interactionType,
|
||||
peerId = peerId,
|
||||
userIds = userIds,
|
||||
@@ -103,35 +115,212 @@ class LongPollUpdatesParser(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseConversationPinStateChanged(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
private fun parseUnreadCounterUpdate(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType $event")
|
||||
|
||||
val peerId = event[1].asInt()
|
||||
val majorId = event[2].asInt()
|
||||
val unreadCount = event[1].asInt()
|
||||
val unreadUnmutedCount = event[2].asInt()
|
||||
val showOnlyMuted = event[3].asInt() == 1
|
||||
val businessNotifyUnreadCount = event[4].asInt()
|
||||
val archiveUnreadCount = event[7].asInt()
|
||||
val archiveUnreadUnmutedCount = event[8].asInt()
|
||||
val archiveMentionsCount = event[9].asInt()
|
||||
|
||||
coroutineScope.launch {
|
||||
listenersMap[ApiEvent.PIN_UNPIN_CONVERSATION]?.let { listeners ->
|
||||
listenersMap[LongPollEvent.UNREAD_COUNTER_UPDATE]?.let { listeners ->
|
||||
listeners.forEach { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.VkConversationPinStateChangedEvent>)
|
||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.UnreadCounter>)
|
||||
.onEvent(
|
||||
LongPollEvent.VkConversationPinStateChangedEvent(
|
||||
peerId = peerId,
|
||||
majorId = majorId
|
||||
LongPollParsedEvent.UnreadCounter(
|
||||
unread = unreadCount,
|
||||
unreadUnmuted = unreadUnmutedCount,
|
||||
showOnlyMuted = showOnlyMuted,
|
||||
business = businessNotifyUnreadCount,
|
||||
archive = archiveUnreadCount,
|
||||
archiveUnmuted = archiveUnreadUnmutedCount,
|
||||
archiveMentions = archiveMentionsCount
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseMessageSetFlags(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
|
||||
val messageId = event[1].asInt()
|
||||
val flags = event[2].asInt()
|
||||
val peerId = event[3].asInt()
|
||||
|
||||
val eventsToSend = mutableListOf<LongPollParsedEvent>()
|
||||
|
||||
val parsedFlags = MessageFlags.parse(flags)
|
||||
parsedFlags.forEach { flag ->
|
||||
when (flag) {
|
||||
MessageFlags.IMPORTANT -> { // marked as important
|
||||
val eventToSend = LongPollParsedEvent.MessageMarkedAsImportant(
|
||||
peerId = peerId,
|
||||
messageId = messageId,
|
||||
marked = true
|
||||
)
|
||||
eventsToSend += eventToSend
|
||||
|
||||
listenersMap[LongPollEvent.MARKED_AS_IMPORTANT]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageMarkedAsImportant>)
|
||||
?.onEvent(eventToSend)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageFlags.SPAM -> { // marked as spam
|
||||
val eventToSend = LongPollParsedEvent.MessageMarkedAsSpam(
|
||||
peerId = peerId,
|
||||
messageId = messageId
|
||||
)
|
||||
eventsToSend += eventToSend
|
||||
|
||||
listenersMap[LongPollEvent.MARKED_AS_SPAM]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageMarkedAsSpam>)
|
||||
?.onEvent(eventToSend)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageFlags.DELETED -> {
|
||||
val eventToSend =
|
||||
if (parsedFlags.contains(MessageFlags.DELETED_FOR_ALL)) { // deleted for all
|
||||
LongPollParsedEvent.MessageDeleted(
|
||||
peerId = peerId,
|
||||
messageId = messageId,
|
||||
forAll = true
|
||||
)
|
||||
} else { // deleted only for me
|
||||
LongPollParsedEvent.MessageDeleted(
|
||||
peerId = peerId,
|
||||
messageId = messageId,
|
||||
forAll = false
|
||||
)
|
||||
}
|
||||
eventsToSend += eventToSend
|
||||
|
||||
listenersMap[LongPollEvent.MESSAGE_DELETED]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageDeleted>)
|
||||
?.onEvent(eventToSend)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageFlags.AUDIO_LISTENED -> { // audio message listened
|
||||
val eventToSend = LongPollParsedEvent.AudioMessageListened(
|
||||
peerId = peerId,
|
||||
messageId = messageId
|
||||
)
|
||||
eventsToSend += eventToSend
|
||||
|
||||
listenersMap[LongPollEvent.AUDIO_MESSAGE_LISTENED]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.AudioMessageListened>)
|
||||
?.onEvent(eventToSend)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
eventsToSend.forEach { eventToSend ->
|
||||
listenersMap[LongPollEvent.MESSAGE_SET_FLAGS]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent>)?.onEvent(eventToSend)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseMessageClearFlags(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
|
||||
val messageId = event[1].asInt()
|
||||
val flags = event[2].asInt()
|
||||
val peerId = event[3].asInt()
|
||||
|
||||
val eventsToSend = mutableListOf<LongPollParsedEvent>()
|
||||
|
||||
val parsedFlags = MessageFlags.parse(flags)
|
||||
|
||||
coroutineScope.launch {
|
||||
parsedFlags.forEach { flag ->
|
||||
when (flag) {
|
||||
MessageFlags.IMPORTANT -> { // not important anymore
|
||||
val eventToSend = LongPollParsedEvent.MessageMarkedAsImportant(
|
||||
peerId = peerId,
|
||||
messageId = messageId,
|
||||
marked = false
|
||||
)
|
||||
eventsToSend += eventToSend
|
||||
|
||||
listenersMap[LongPollEvent.MARKED_AS_IMPORTANT]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageMarkedAsImportant>)
|
||||
?.onEvent(eventToSend)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageFlags.SPAM -> {
|
||||
if (parsedFlags.contains(MessageFlags.CANCEL_SPAM)) { // not spam anymore
|
||||
withContext(Dispatchers.IO) {
|
||||
val message = loadMessage(messageId)
|
||||
message?.let {
|
||||
val eventToSend =
|
||||
LongPollParsedEvent.MessageMarkedAsNotSpam(message = message)
|
||||
eventsToSend += eventToSend
|
||||
|
||||
listenersMap[LongPollEvent.MARKED_AS_NOT_SPAM]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageMarkedAsNotSpam>)
|
||||
?.onEvent(eventToSend)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageFlags.DELETED -> { // restored
|
||||
withContext(Dispatchers.IO) {
|
||||
val message = loadMessage(messageId)
|
||||
message?.let {
|
||||
val eventToSend =
|
||||
LongPollParsedEvent.MessageRestored(message = message)
|
||||
eventsToSend += eventToSend
|
||||
|
||||
listenersMap[LongPollEvent.MESSAGE_RESTORED]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as? VkEventCallback<LongPollParsedEvent.MessageRestored>)
|
||||
?.onEvent(eventToSend)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
|
||||
eventsToSend.forEach { eventToSend ->
|
||||
listenersMap[LongPollEvent.MESSAGE_CLEAR_FLAGS]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
vkEventCallback.onEvent(eventToSend)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseMessageNew(eventType: ApiEvent, event: List<Any>) {
|
||||
@@ -139,17 +328,11 @@ class LongPollUpdatesParser(
|
||||
val messageId = event[1].asInt()
|
||||
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
val newMessageEvent: LongPollEvent.VkMessageNewEvent? =
|
||||
loadNormalMessage(
|
||||
eventType,
|
||||
messageId
|
||||
)
|
||||
|
||||
newMessageEvent?.let { event ->
|
||||
listenersMap[ApiEvent.MESSAGE_NEW]?.let {
|
||||
loadMessage(messageId)?.let { message ->
|
||||
listenersMap[LongPollEvent.MESSAGE_NEW]?.let {
|
||||
it.map { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.VkMessageNewEvent>)
|
||||
.onEvent(event)
|
||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.NewMessage>)
|
||||
.onEvent(LongPollParsedEvent.NewMessage(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,18 +343,12 @@ class LongPollUpdatesParser(
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
val messageId = event[1].asInt()
|
||||
|
||||
coroutineScope.launch {
|
||||
val editedMessageEvent: LongPollEvent.VkMessageEditEvent? =
|
||||
loadNormalMessage(
|
||||
eventType,
|
||||
messageId
|
||||
)
|
||||
|
||||
editedMessageEvent?.let { event ->
|
||||
listenersMap[ApiEvent.MESSAGE_EDIT]?.let {
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
loadMessage(messageId)?.let { message ->
|
||||
listenersMap[LongPollEvent.MESSAGE_EDITED]?.let {
|
||||
it.map { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.VkMessageEditEvent>)
|
||||
.onEvent(event)
|
||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.MessageEdited>)
|
||||
.onEvent(LongPollParsedEvent.MessageEdited(message))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -184,12 +361,11 @@ class LongPollUpdatesParser(
|
||||
val messageId = event[2].asInt()
|
||||
val unreadCount = event[3].asInt()
|
||||
|
||||
coroutineScope.launch {
|
||||
listenersMap[ApiEvent.MESSAGE_READ_INCOMING]?.let { listeners ->
|
||||
listenersMap[LongPollEvent.INCOMING_MESSAGE_READ]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.VkMessageReadIncomingEvent>)
|
||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.IncomingMessageRead>)
|
||||
.onEvent(
|
||||
LongPollEvent.VkMessageReadIncomingEvent(
|
||||
LongPollParsedEvent.IncomingMessageRead(
|
||||
peerId = peerId,
|
||||
messageId = messageId,
|
||||
unreadCount = unreadCount
|
||||
@@ -198,7 +374,6 @@ class LongPollUpdatesParser(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseMessageReadOutgoing(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
@@ -206,12 +381,11 @@ class LongPollUpdatesParser(
|
||||
val messageId = event[2].asInt()
|
||||
val unreadCount = event[3].asInt()
|
||||
|
||||
coroutineScope.launch {
|
||||
listenersMap[ApiEvent.MESSAGE_READ_OUTGOING]?.let { listeners ->
|
||||
listenersMap[LongPollEvent.OUTGOING_MESSAGE_READ]?.let { listeners ->
|
||||
listeners.map { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollEvent.VkMessageReadOutgoingEvent>)
|
||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.OutgoingMessageRead>)
|
||||
.onEvent(
|
||||
LongPollEvent.VkMessageReadOutgoingEvent(
|
||||
LongPollParsedEvent.OutgoingMessageRead(
|
||||
peerId = peerId,
|
||||
messageId = messageId,
|
||||
unreadCount = unreadCount
|
||||
@@ -220,16 +394,73 @@ class LongPollUpdatesParser(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseChatClearFlags(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
}
|
||||
|
||||
private fun parseChatSetFlags(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
}
|
||||
|
||||
private fun parseMessagesDeleted(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
|
||||
val peerId = event[1].asInt()
|
||||
val messageId = event[2].asInt()
|
||||
|
||||
listenersMap[LongPollEvent.CHAT_CLEARED]?.let { listeners ->
|
||||
listeners.forEach { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.ChatCleared>)
|
||||
.onEvent(
|
||||
LongPollParsedEvent.ChatCleared(
|
||||
peerId = peerId,
|
||||
toMessageId = messageId
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend inline fun <reified T : LongPollEvent> loadNormalMessage(
|
||||
eventType: ApiEvent,
|
||||
messageId: Int
|
||||
): T? = suspendCoroutine { continuation ->
|
||||
private fun parseChatMajorChanged(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
|
||||
val peerId = event[1].asInt()
|
||||
val majorId = event[2].asInt()
|
||||
|
||||
listenersMap[LongPollEvent.CHAT_MAJOR_CHANGED]?.let { listeners ->
|
||||
listeners.forEach { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.ChatMajorChanged>)
|
||||
.onEvent(
|
||||
LongPollParsedEvent.ChatMajorChanged(
|
||||
peerId = peerId,
|
||||
majorId = majorId,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseChatMinorChanged(eventType: ApiEvent, event: List<Any>) {
|
||||
Log.d("LongPollUpdatesParser", "$eventType: $event")
|
||||
|
||||
val peerId = event[1].asInt()
|
||||
val minorId = event[2].asInt()
|
||||
|
||||
listenersMap[LongPollEvent.CHAT_MINOR_CHANGED]?.let { listeners ->
|
||||
listeners.forEach { vkEventCallback ->
|
||||
(vkEventCallback as VkEventCallback<LongPollParsedEvent.ChatMinorChanged>)
|
||||
.onEvent(
|
||||
LongPollParsedEvent.ChatMinorChanged(
|
||||
peerId = peerId,
|
||||
minorId = minorId,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun loadMessage(messageId: Int): VkMessage? = suspendCoroutine { continuation ->
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
messagesUseCase.getById(
|
||||
messageIds = listOf(messageId),
|
||||
@@ -238,10 +469,11 @@ class LongPollUpdatesParser(
|
||||
).listenValue(this) { state ->
|
||||
state.processState(
|
||||
error = { error ->
|
||||
Log.e("LongPollUpdatesParser", "loadNormalMessage: error: $error")
|
||||
Log.e("LongPollUpdatesParser", "loadMessage: error: $error")
|
||||
continuation.resume(null)
|
||||
},
|
||||
success = { messages ->
|
||||
val message = messages.singleOrNull() ?: run {
|
||||
success = { response ->
|
||||
val message = response.singleOrNull() ?: run {
|
||||
continuation.resume(null)
|
||||
return@listenValue
|
||||
}
|
||||
@@ -249,107 +481,113 @@ class LongPollUpdatesParser(
|
||||
VkMemoryCache[message.id] = message
|
||||
messagesUseCase.storeMessage(message)
|
||||
|
||||
val resumeValue: LongPollEvent? = when (eventType) {
|
||||
ApiEvent.MESSAGE_NEW -> LongPollEvent.VkMessageNewEvent(message)
|
||||
ApiEvent.MESSAGE_EDIT -> LongPollEvent.VkMessageEditEvent(message)
|
||||
|
||||
else -> {
|
||||
continuation.resume(null)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
resumeValue?.let { value -> continuation.resume(value as T) }
|
||||
continuation.resume(message)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Any> registerListener(
|
||||
eventType: ApiEvent,
|
||||
@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) }
|
||||
map[eventType] = (map[eventType] ?: mutableListOf())
|
||||
.also {
|
||||
it.add(listener as VkEventCallback<LongPollParsedEvent>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T : Any> registerListeners(
|
||||
eventTypes: List<ApiEvent>,
|
||||
private fun <T : LongPollParsedEvent> registerListeners(
|
||||
eventTypes: List<LongPollEvent>,
|
||||
listener: VkEventCallback<T>
|
||||
) {
|
||||
eventTypes.forEach { eventType -> registerListener(eventType, listener) }
|
||||
}
|
||||
|
||||
fun onConversationPinStateChanged(listener: VkEventCallback<LongPollEvent.VkConversationPinStateChangedEvent>) {
|
||||
registerListener(ApiEvent.PIN_UNPIN_CONVERSATION, listener)
|
||||
fun onMessageSetFlags(block: (LongPollParsedEvent) -> Unit) {
|
||||
registerListener(LongPollEvent.MESSAGE_SET_FLAGS, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onConversationPinStateChanged(block: (LongPollEvent.VkConversationPinStateChangedEvent) -> Unit) {
|
||||
onConversationPinStateChanged(assembleEventCallback(block))
|
||||
fun onMessageMarkedAsImportant(block: (LongPollParsedEvent.MessageMarkedAsImportant) -> Unit) {
|
||||
registerListener(LongPollEvent.MARKED_AS_IMPORTANT, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onMessageIncomingRead(listener: VkEventCallback<LongPollEvent.VkMessageReadIncomingEvent>) {
|
||||
registerListener(ApiEvent.MESSAGE_READ_INCOMING, listener)
|
||||
fun onMessageMarkedAsSpam(block: (LongPollParsedEvent.MessageMarkedAsSpam) -> Unit) {
|
||||
registerListener(LongPollEvent.MARKED_AS_SPAM, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onMessageIncomingRead(block: (LongPollEvent.VkMessageReadIncomingEvent) -> Unit) {
|
||||
onMessageIncomingRead(assembleEventCallback(block))
|
||||
fun onMessageDeleted(block: (LongPollParsedEvent.MessageDeleted) -> Unit) {
|
||||
registerListener(LongPollEvent.MESSAGE_DELETED, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onMessageOutgoingRead(listener: VkEventCallback<LongPollEvent.VkMessageReadOutgoingEvent>) {
|
||||
registerListener(ApiEvent.MESSAGE_READ_OUTGOING, listener)
|
||||
fun onMessageClearFlags(block: (LongPollParsedEvent) -> Unit) {
|
||||
registerListener(LongPollEvent.MESSAGE_CLEAR_FLAGS, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onMessageOutgoingRead(block: (LongPollEvent.VkMessageReadOutgoingEvent) -> Unit) {
|
||||
onMessageOutgoingRead(assembleEventCallback(block))
|
||||
fun onMessageMarkedAsNotSpam(block: (LongPollParsedEvent.MessageMarkedAsNotSpam) -> Unit) {
|
||||
registerListener(LongPollEvent.MARKED_AS_NOT_SPAM, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onNewMessage(listener: VkEventCallback<LongPollEvent.VkMessageNewEvent>) {
|
||||
registerListener(ApiEvent.MESSAGE_NEW, listener)
|
||||
fun onMessageRestored(block: (LongPollParsedEvent.MessageRestored) -> Unit) {
|
||||
registerListener(LongPollEvent.MESSAGE_RESTORED, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onNewMessage(block: (LongPollEvent.VkMessageNewEvent) -> Unit) {
|
||||
onNewMessage(assembleEventCallback(block))
|
||||
fun onNewMessage(block: (LongPollParsedEvent.NewMessage) -> Unit) {
|
||||
registerListener(LongPollEvent.MESSAGE_NEW, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onMessageEdited(listener: VkEventCallback<LongPollEvent.VkMessageEditEvent>) {
|
||||
registerListener(ApiEvent.MESSAGE_EDIT, listener)
|
||||
fun onMessageEdited(block: (LongPollParsedEvent.MessageEdited) -> Unit) {
|
||||
registerListener(LongPollEvent.MESSAGE_EDITED, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onMessageEdited(block: (LongPollEvent.VkMessageEditEvent) -> Unit) {
|
||||
onMessageEdited(assembleEventCallback(block))
|
||||
fun onMessageIncomingRead(block: (LongPollParsedEvent.IncomingMessageRead) -> Unit) {
|
||||
registerListener(LongPollEvent.INCOMING_MESSAGE_READ, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onInteractions(listener: VkEventCallback<LongPollEvent.Interaction>) {
|
||||
fun onMessageOutgoingRead(block: (LongPollParsedEvent.OutgoingMessageRead) -> Unit) {
|
||||
registerListener(LongPollEvent.OUTGOING_MESSAGE_READ, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onChatCleared(block: (LongPollParsedEvent.ChatCleared) -> Unit) {
|
||||
registerListener(LongPollEvent.CHAT_CLEARED, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onChatMajorChanged(block: (LongPollParsedEvent.ChatMajorChanged) -> Unit) {
|
||||
registerListener(LongPollEvent.CHAT_MAJOR_CHANGED, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onChatMinorChanged(block: (LongPollParsedEvent.ChatMinorChanged) -> Unit) {
|
||||
registerListener(LongPollEvent.CHAT_MINOR_CHANGED, assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun onInteractions(block: (LongPollParsedEvent.Interaction) -> Unit) {
|
||||
registerListeners(
|
||||
eventTypes = listOf(
|
||||
ApiEvent.TYPING,
|
||||
ApiEvent.AUDIO_MESSAGE_RECORDING,
|
||||
ApiEvent.PHOTO_UPLOADING,
|
||||
ApiEvent.VIDEO_UPLOADING,
|
||||
ApiEvent.FILE_UPLOADING
|
||||
LongPollEvent.TYPING,
|
||||
LongPollEvent.AUDIO_MESSAGE_RECORDING,
|
||||
LongPollEvent.PHOTO_UPLOADING,
|
||||
LongPollEvent.VIDEO_UPLOADING,
|
||||
LongPollEvent.FILE_UPLOADING
|
||||
),
|
||||
listener = listener
|
||||
listener = assembleEventCallback(block)
|
||||
)
|
||||
}
|
||||
|
||||
fun onInteractions(block: (LongPollEvent.Interaction) -> Unit) {
|
||||
onInteractions(assembleEventCallback(block))
|
||||
}
|
||||
|
||||
fun clearListeners() {
|
||||
listenersMap.clear()
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <R : Any> assembleEventCallback(
|
||||
internal inline fun <R : LongPollParsedEvent> assembleEventCallback(
|
||||
crossinline block: (R) -> Unit,
|
||||
): VkEventCallback<R> {
|
||||
return VkEventCallback { event -> block.invoke(event) }
|
||||
}
|
||||
|
||||
fun interface VkEventCallback<in T : Any> {
|
||||
fun interface VkEventCallback<in T : LongPollParsedEvent> {
|
||||
fun onEvent(event: T)
|
||||
}
|
||||
|
||||
@@ -7,8 +7,11 @@ enum class ApiEvent(val value: Int) {
|
||||
MESSAGE_EDIT(5),
|
||||
MESSAGE_READ_INCOMING(6),
|
||||
MESSAGE_READ_OUTGOING(7),
|
||||
CHAT_CLEAR_FLAGS(10),
|
||||
CHAT_SET_FLAGS(12),
|
||||
MESSAGES_DELETED(13),
|
||||
PIN_UNPIN_CONVERSATION(20),
|
||||
CHAT_MAJOR_CHANGED(20),
|
||||
CHAT_MINOR_CHANGED(21),
|
||||
TYPING(63),
|
||||
AUDIO_MESSAGE_RECORDING(64),
|
||||
PHOTO_UPLOADING(65),
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package dev.meloda.fast.model
|
||||
|
||||
enum class ConversationFlags(val value: Int) {
|
||||
DISABLE_PUSH(16),
|
||||
DISABLE_SOUND(32),
|
||||
INCOMING_CHAT_REQUEST(256),
|
||||
DECLINED_CHAT_REQUEST(512),
|
||||
MENTION(1024),
|
||||
HIDE_CHAT_FROM_SEARCH(2048),
|
||||
BUSINESS_CHAT(8192),
|
||||
MARKED_MESSAGE(16384), // mention or disappearing message
|
||||
DO_NOT_NOTIFY_MENTIONS_ALL_ONLINE(262144),
|
||||
DO_NOT_NOTIFY_ALL_MENTIONS(524288),
|
||||
MARKED_AS_UNREAD(1048576),
|
||||
ARCHIVED(8388608),
|
||||
CALL_IN_PROGRESS(16777216),
|
||||
}
|
||||
@@ -1,35 +1,27 @@
|
||||
package dev.meloda.fast.model
|
||||
|
||||
import dev.meloda.fast.model.api.domain.VkMessage
|
||||
|
||||
sealed interface LongPollEvent {
|
||||
|
||||
data class VkMessageNewEvent(val message: VkMessage) : LongPollEvent
|
||||
|
||||
data class VkMessageEditEvent(val message: VkMessage) : LongPollEvent
|
||||
|
||||
data class VkMessageReadIncomingEvent(
|
||||
val peerId: Int,
|
||||
val messageId: Int,
|
||||
val unreadCount: Int,
|
||||
) : LongPollEvent
|
||||
|
||||
data class VkMessageReadOutgoingEvent(
|
||||
val peerId: Int,
|
||||
val messageId: Int,
|
||||
val unreadCount: Int,
|
||||
) : LongPollEvent
|
||||
|
||||
data class VkConversationPinStateChangedEvent(
|
||||
val peerId: Int,
|
||||
val majorId: Int,
|
||||
) : LongPollEvent
|
||||
|
||||
data class Interaction(
|
||||
val interactionType: InteractionType,
|
||||
val peerId: Int,
|
||||
val userIds: List<Int>,
|
||||
val totalCount: Int,
|
||||
val timestamp: Int
|
||||
) : LongPollEvent
|
||||
enum class LongPollEvent {
|
||||
MESSAGE_SET_FLAGS,
|
||||
MESSAGE_CLEAR_FLAGS,
|
||||
MESSAGE_NEW,
|
||||
MESSAGE_EDITED,
|
||||
INCOMING_MESSAGE_READ,
|
||||
OUTGOING_MESSAGE_READ,
|
||||
CHAT_SET_FLAGS,
|
||||
CHAT_CLEAR_FLAGS,
|
||||
CHAT_MAJOR_CHANGED,
|
||||
CHAT_MINOR_CHANGED,
|
||||
TYPING,
|
||||
AUDIO_MESSAGE_RECORDING,
|
||||
PHOTO_UPLOADING,
|
||||
VIDEO_UPLOADING,
|
||||
FILE_UPLOADING,
|
||||
UNREAD_COUNTER_UPDATE,
|
||||
MARKED_AS_IMPORTANT,
|
||||
MARKED_AS_SPAM,
|
||||
MARKED_AS_NOT_SPAM,
|
||||
MESSAGE_DELETED,
|
||||
MESSAGE_RESTORED,
|
||||
AUDIO_MESSAGE_LISTENED,
|
||||
CHAT_CLEARED
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package dev.meloda.fast.model
|
||||
|
||||
import dev.meloda.fast.model.api.domain.VkMessage
|
||||
|
||||
sealed interface LongPollParsedEvent {
|
||||
|
||||
data class NewMessage(val message: VkMessage) : LongPollParsedEvent
|
||||
|
||||
data class MessageEdited(val message: VkMessage) : LongPollParsedEvent
|
||||
|
||||
data class IncomingMessageRead(
|
||||
val peerId: Int,
|
||||
val messageId: Int,
|
||||
val unreadCount: Int,
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class OutgoingMessageRead(
|
||||
val peerId: Int,
|
||||
val messageId: Int,
|
||||
val unreadCount: Int,
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class ChatMajorChanged(
|
||||
val peerId: Int,
|
||||
val majorId: Int,
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class ChatMinorChanged(
|
||||
val peerId: Int,
|
||||
val minorId: Int
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class Interaction(
|
||||
val interactionType: InteractionType,
|
||||
val peerId: Int,
|
||||
val userIds: List<Int>,
|
||||
val totalCount: Int,
|
||||
val timestamp: Int
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class UnreadCounter(
|
||||
val unread: Int,
|
||||
val unreadUnmuted: Int,
|
||||
val showOnlyMuted: Boolean,
|
||||
val business: Int,
|
||||
val archive: Int,
|
||||
val archiveUnmuted: Int,
|
||||
val archiveMentions: Int
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class MessageMarkedAsImportant(
|
||||
val peerId: Int,
|
||||
val messageId: Int,
|
||||
val marked: Boolean
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class MessageMarkedAsSpam(
|
||||
val peerId: Int,
|
||||
val messageId: Int
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class MessageMarkedAsNotSpam(
|
||||
val message: VkMessage
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class MessageDeleted(
|
||||
val peerId: Int,
|
||||
val messageId: Int,
|
||||
val forAll: Boolean
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class MessageRestored(
|
||||
val message: VkMessage
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class AudioMessageListened(
|
||||
val peerId: Int,
|
||||
val messageId: Int
|
||||
) : LongPollParsedEvent
|
||||
|
||||
data class ChatCleared(
|
||||
val peerId: Int,
|
||||
val toMessageId: Int
|
||||
): LongPollParsedEvent
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package dev.meloda.fast.model
|
||||
|
||||
enum class MessageFlags(val value: Int) {
|
||||
UNREAD(1),
|
||||
OUTGOING(2),
|
||||
IMPORTANT(8),
|
||||
SPAM(64),
|
||||
DELETED(128),
|
||||
AUDIO_LISTENED(4096),
|
||||
FROM_GROUP_CHAT(8192),
|
||||
CANCEL_SPAM(32768),
|
||||
DELETED_FOR_ALL(131072),
|
||||
DO_NOT_SHOW_NOTIFICATION(1048576),
|
||||
MESSAGE_WITH_REPLY(2097152),
|
||||
REACTION(16777216);
|
||||
|
||||
companion object {
|
||||
|
||||
fun parse(mask: Int): List<MessageFlags> {
|
||||
val flags = mutableListOf<MessageFlags>()
|
||||
|
||||
entries.forEach { flag ->
|
||||
if (mask and flag.value > 0) {
|
||||
flags.add(flag)
|
||||
}
|
||||
}
|
||||
|
||||
return flags
|
||||
}
|
||||
}
|
||||
}
|
||||
+115
-78
@@ -23,7 +23,7 @@ import dev.meloda.fast.domain.LongPollUpdatesParser
|
||||
import dev.meloda.fast.domain.MessagesUseCase
|
||||
import dev.meloda.fast.model.BaseError
|
||||
import dev.meloda.fast.model.InteractionType
|
||||
import dev.meloda.fast.model.LongPollEvent
|
||||
import dev.meloda.fast.model.LongPollParsedEvent
|
||||
import dev.meloda.fast.model.api.domain.VkConversation
|
||||
import dev.meloda.fast.network.VkErrorCode
|
||||
import dev.meloda.fast.ui.model.api.ConversationOption
|
||||
@@ -104,8 +104,10 @@ class ConversationsViewModelImpl(
|
||||
updatesParser.onMessageEdited(::handleEditedMessage)
|
||||
updatesParser.onMessageIncomingRead(::handleReadIncomingMessage)
|
||||
updatesParser.onMessageOutgoingRead(::handleReadOutgoingMessage)
|
||||
updatesParser.onConversationPinStateChanged(::handlePinStateChanged)
|
||||
updatesParser.onInteractions(::handleInteraction)
|
||||
updatesParser.onChatMajorChanged(::handleChatMajorChanged)
|
||||
updatesParser.onChatMinorChanged(::handleChatMinorChanged)
|
||||
updatesParser.onChatCleared(::handleChatClearing)
|
||||
|
||||
loadConversations()
|
||||
}
|
||||
@@ -348,9 +350,7 @@ class ConversationsViewModelImpl(
|
||||
private fun deleteConversation(peerId: Int) {
|
||||
conversationsUseCase.delete(peerId).listenValue(viewModelScope) { state ->
|
||||
state.processState(
|
||||
error = { error ->
|
||||
|
||||
},
|
||||
error = {},
|
||||
success = {
|
||||
val newConversations = conversations.value.toMutableList()
|
||||
val conversationIndex =
|
||||
@@ -359,16 +359,7 @@ class ConversationsViewModelImpl(
|
||||
|
||||
newConversations.removeAt(conversationIndex)
|
||||
conversations.update { newConversations }
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
conversations = newConversations.map {
|
||||
it.asPresentation(
|
||||
resources = resources,
|
||||
useContactName = useContactNames
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
sortConversations()
|
||||
}
|
||||
)
|
||||
screenState.emit(screenState.value.copy(isLoading = state.isLoading()))
|
||||
@@ -379,15 +370,13 @@ class ConversationsViewModelImpl(
|
||||
conversationsUseCase.changePinState(peerId, pin)
|
||||
.listenValue(viewModelScope) { state ->
|
||||
state.processState(
|
||||
error = { error ->
|
||||
|
||||
},
|
||||
error = {},
|
||||
success = {
|
||||
handlePinStateChanged(
|
||||
LongPollEvent.VkConversationPinStateChangedEvent(
|
||||
handleChatMajorChanged(
|
||||
LongPollParsedEvent.ChatMajorChanged(
|
||||
peerId = peerId,
|
||||
majorId = if (pin) {
|
||||
(pinnedConversationsCount.value + 1) * 16
|
||||
pinnedConversationsCount.value.plus(1) * 16
|
||||
} else {
|
||||
0
|
||||
}
|
||||
@@ -400,7 +389,7 @@ class ConversationsViewModelImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleNewMessage(event: LongPollEvent.VkMessageNewEvent) {
|
||||
private fun handleNewMessage(event: LongPollParsedEvent.NewMessage) {
|
||||
val message = event.message
|
||||
|
||||
val newConversations = conversations.value.toMutableList()
|
||||
@@ -411,28 +400,14 @@ class ConversationsViewModelImpl(
|
||||
loadConversationsByIdUseCase(peerIds = listOf(message.peerId))
|
||||
.listenValue(viewModelScope) { state ->
|
||||
state.processState(
|
||||
error = { error ->
|
||||
|
||||
},
|
||||
error = {},
|
||||
success = { response ->
|
||||
val conversation = (response.firstOrNull() ?: return@listenValue)
|
||||
.copy(lastMessage = message)
|
||||
|
||||
// TODO: 22-Dec-24, Danil Nikolaev: handle interactions and pinned state
|
||||
|
||||
newConversations.add(pinnedConversationsCount.value, conversation)
|
||||
conversations.update { newConversations }
|
||||
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
conversations = newConversations.map {
|
||||
it.asPresentation(
|
||||
resources = resources,
|
||||
useContactName = useContactNames
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
sortConversations()
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -487,7 +462,7 @@ class ConversationsViewModelImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleEditedMessage(event: LongPollEvent.VkMessageEditEvent) {
|
||||
private fun handleEditedMessage(event: LongPollParsedEvent.MessageEdited) {
|
||||
val message = event.message
|
||||
val newConversations = conversations.value.toMutableList()
|
||||
|
||||
@@ -516,7 +491,7 @@ class ConversationsViewModelImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReadIncomingMessage(event: LongPollEvent.VkMessageReadIncomingEvent) {
|
||||
private fun handleReadIncomingMessage(event: LongPollParsedEvent.IncomingMessageRead) {
|
||||
val newConversations = conversations.value.toMutableList()
|
||||
|
||||
val conversationIndex =
|
||||
@@ -546,7 +521,7 @@ class ConversationsViewModelImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReadOutgoingMessage(event: LongPollEvent.VkMessageReadOutgoingEvent) {
|
||||
private fun handleReadOutgoingMessage(event: LongPollParsedEvent.OutgoingMessageRead) {
|
||||
val newConversations = conversations.value.toMutableList()
|
||||
|
||||
val conversationIndex =
|
||||
@@ -575,47 +550,113 @@ class ConversationsViewModelImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePinStateChanged(event: LongPollEvent.VkConversationPinStateChangedEvent) {
|
||||
var pinnedCount = pinnedConversationsCount.value
|
||||
private fun handleChatMajorChanged(event: LongPollParsedEvent.ChatMajorChanged) {
|
||||
val newConversations = conversations.value.toMutableList()
|
||||
|
||||
val conversationIndex =
|
||||
newConversations.indexOfFirstOrNull { it.id == event.peerId }
|
||||
|
||||
if (conversationIndex == null) { // диалога нет в списке
|
||||
// pizdets
|
||||
} else {
|
||||
val pin = event.majorId > 0
|
||||
|
||||
val conversation = newConversations[conversationIndex].copy(majorId = event.majorId)
|
||||
|
||||
newConversations.removeAt(conversationIndex)
|
||||
|
||||
if (pin) {
|
||||
newConversations.add(0, conversation)
|
||||
} else {
|
||||
pinnedCount -= 1
|
||||
|
||||
newConversations.add(conversation)
|
||||
|
||||
val pinnedSubList = newConversations.filter(VkConversation::isPinned)
|
||||
val unpinnedSubList = newConversations
|
||||
.filterNot(VkConversation::isPinned)
|
||||
.sortedByDescending { it.lastMessage?.date }
|
||||
|
||||
newConversations.clear()
|
||||
newConversations += pinnedSubList + unpinnedSubList
|
||||
}
|
||||
|
||||
conversations.update { newConversations }
|
||||
newConversations[conversationIndex] =
|
||||
newConversations[conversationIndex].copy(majorId = event.majorId)
|
||||
|
||||
conversations.setValue { newConversations }
|
||||
screenState.setValue { old ->
|
||||
old.copy(conversations = newConversations.map {
|
||||
old.copy(
|
||||
conversations = newConversations.map {
|
||||
it.asPresentation(
|
||||
resources = resources,
|
||||
useContactName = useContactNames
|
||||
)
|
||||
})
|
||||
}
|
||||
)
|
||||
}
|
||||
sortConversations()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleChatMinorChanged(event: LongPollParsedEvent.ChatMinorChanged) {
|
||||
val newConversations = conversations.value.toMutableList()
|
||||
val conversationIndex =
|
||||
newConversations.indexOfFirstOrNull { it.id == event.peerId }
|
||||
|
||||
if (conversationIndex == null) { // диалога нет в списке
|
||||
// pizdets
|
||||
} else {
|
||||
newConversations[conversationIndex] =
|
||||
newConversations[conversationIndex].copy(minorId = event.minorId)
|
||||
|
||||
conversations.setValue { newConversations }
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
conversations = newConversations.map {
|
||||
it.asPresentation(
|
||||
resources = resources,
|
||||
useContactName = useContactNames
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
sortConversations()
|
||||
}
|
||||
}
|
||||
|
||||
private fun sortConversations() {
|
||||
val newConversations = conversations.value.toMutableList()
|
||||
val pinnedConversations = newConversations
|
||||
.filter(VkConversation::isPinned)
|
||||
.sortedWith { c1, c2 ->
|
||||
val diff = c2.majorId - c1.majorId
|
||||
|
||||
if (diff == 0) {
|
||||
c2.minorId - c1.minorId
|
||||
} else {
|
||||
diff
|
||||
}
|
||||
}
|
||||
|
||||
newConversations.removeAll(pinnedConversations)
|
||||
newConversations.sortWith { c1, c2 ->
|
||||
(c2.lastMessage?.date ?: 0) - (c1.lastMessage?.date ?: 0)
|
||||
}
|
||||
|
||||
newConversations.addAll(0, pinnedConversations)
|
||||
|
||||
conversations.update { newConversations }
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
conversations = newConversations.map {
|
||||
it.asPresentation(
|
||||
resources = resources,
|
||||
useContactName = useContactNames
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleChatClearing(event: LongPollParsedEvent.ChatCleared) {
|
||||
val newConversations = conversations.value.toMutableList()
|
||||
|
||||
val conversationIndex = newConversations.indexOfFirstOrNull { it.id == event.peerId }
|
||||
|
||||
if (conversationIndex == null) { // диалога нет в списке
|
||||
// pizdets
|
||||
} else {
|
||||
newConversations.removeAt(conversationIndex)
|
||||
|
||||
conversations.setValue { newConversations }
|
||||
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
conversations = newConversations.map {
|
||||
it.asPresentation(
|
||||
resources = resources,
|
||||
useContactName = useContactNames
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -627,9 +668,9 @@ class ConversationsViewModelImpl(
|
||||
val timerJob: Job
|
||||
)
|
||||
|
||||
private object NewInteractionException : CancellationException()
|
||||
private class NewInteractionException : CancellationException()
|
||||
|
||||
private fun handleInteraction(event: LongPollEvent.Interaction) {
|
||||
private fun handleInteraction(event: LongPollParsedEvent.Interaction) {
|
||||
val interactionType = event.interactionType
|
||||
val peerId = event.peerId
|
||||
val userIds = event.userIds
|
||||
@@ -638,9 +679,7 @@ class ConversationsViewModelImpl(
|
||||
val conversationAndIndex =
|
||||
newConversations.findWithIndex { it.id == peerId }
|
||||
|
||||
if (conversationAndIndex == null) { // диалога нет в списке
|
||||
// pizdets
|
||||
} else {
|
||||
if (conversationAndIndex != null) {
|
||||
newConversations[conversationAndIndex.first] =
|
||||
conversationAndIndex.second.copy(
|
||||
interactionType = interactionType.value,
|
||||
@@ -662,7 +701,7 @@ class ConversationsViewModelImpl(
|
||||
|
||||
interactionsTimers[peerId]?.let { interactionJob ->
|
||||
if (interactionJob.interactionType == interactionType) {
|
||||
interactionJob.timerJob.cancel(NewInteractionException)
|
||||
interactionJob.timerJob.cancel(NewInteractionException())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -721,9 +760,7 @@ class ConversationsViewModelImpl(
|
||||
startMessageId = startMessageId
|
||||
).listenValue(viewModelScope) { state ->
|
||||
state.processState(
|
||||
error = { error ->
|
||||
|
||||
},
|
||||
error = {},
|
||||
success = {
|
||||
val newConversations = conversations.value.toMutableList()
|
||||
val conversationIndex =
|
||||
|
||||
+35
-5
@@ -32,7 +32,7 @@ import dev.meloda.fast.messageshistory.util.extractAvatar
|
||||
import dev.meloda.fast.messageshistory.util.extractTitle
|
||||
import dev.meloda.fast.messageshistory.util.findMessageById
|
||||
import dev.meloda.fast.model.BaseError
|
||||
import dev.meloda.fast.model.LongPollEvent
|
||||
import dev.meloda.fast.model.LongPollParsedEvent
|
||||
import dev.meloda.fast.model.api.domain.VkAttachment
|
||||
import dev.meloda.fast.model.api.domain.VkMessage
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -158,7 +158,7 @@ class MessagesHistoryViewModelImpl(
|
||||
loadMessagesHistory()
|
||||
}
|
||||
|
||||
private fun handleNewMessage(event: LongPollEvent.VkMessageNewEvent) {
|
||||
private fun handleNewMessage(event: LongPollParsedEvent.NewMessage) {
|
||||
val message = event.message
|
||||
|
||||
Log.d("MessagesHistoryViewModel", "handleNewMessage: $message")
|
||||
@@ -200,7 +200,7 @@ class MessagesHistoryViewModelImpl(
|
||||
screenState.setValue { old -> old.copy(messages = newMessages) }
|
||||
}
|
||||
|
||||
private fun handleEditedMessage(event: LongPollEvent.VkMessageEditEvent) {
|
||||
private fun handleEditedMessage(event: LongPollParsedEvent.MessageEdited) {
|
||||
val message = event.message
|
||||
if (message.peerId != screenState.value.conversationId) return
|
||||
|
||||
@@ -223,11 +223,41 @@ class MessagesHistoryViewModelImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReadIncomingEvent(event: LongPollEvent.VkMessageReadIncomingEvent) {
|
||||
private fun handleReadIncomingEvent(event: LongPollParsedEvent.IncomingMessageRead) {
|
||||
if (event.peerId != screenState.value.conversationId) return
|
||||
|
||||
val messages = messages.value
|
||||
val messageIndex =
|
||||
messages.indexOfFirstOrNull { it.id == event.messageId }
|
||||
|
||||
if (messageIndex == null) { // диалога нет в списке
|
||||
// pizdets
|
||||
} else {
|
||||
val newConversation = screenState.value.conversation.copy(
|
||||
inRead = event.messageId
|
||||
)
|
||||
|
||||
val uiMessages = messages.mapIndexed { index, item ->
|
||||
item.asPresentation(
|
||||
resourceProvider = resourceProvider,
|
||||
showName = false,
|
||||
prevMessage = messages.getOrNull(index + 1),
|
||||
nextMessage = messages.getOrNull(index - 1),
|
||||
showTimeInActionMessages = userSettings.showTimeInActionMessages.value,
|
||||
conversation = newConversation
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleReadOutgoingEvent(event: LongPollEvent.VkMessageReadOutgoingEvent) {
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
conversation = newConversation,
|
||||
messages = uiMessages,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleReadOutgoingEvent(event: LongPollParsedEvent.OutgoingMessageRead) {
|
||||
if (event.peerId != screenState.value.conversationId) return
|
||||
|
||||
val messages = messages.value
|
||||
|
||||
Reference in New Issue
Block a user