From 68fff3ebee130389d234a11b710c566e4cc6ea44 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 14 May 2026 20:48:50 +0300 Subject: [PATCH] refactor: extract message interactions --- .../MessagesHistoryInteractionHandler.kt | 246 +++++++++++++++++ .../MessagesHistoryViewModelImpl.kt | 247 ++---------------- 2 files changed, 272 insertions(+), 221 deletions(-) create mode 100644 feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryInteractionHandler.kt diff --git a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryInteractionHandler.kt b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryInteractionHandler.kt new file mode 100644 index 00000000..83a7270b --- /dev/null +++ b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryInteractionHandler.kt @@ -0,0 +1,246 @@ +package dev.meloda.fast.messageshistory + +import android.os.Bundle +import dev.meloda.fast.common.extensions.getParcelableCompat +import dev.meloda.fast.common.extensions.setValue +import dev.meloda.fast.messageshistory.model.MessageDialog +import dev.meloda.fast.messageshistory.model.MessageOption +import dev.meloda.fast.messageshistory.model.MessagesHistoryScreenState +import dev.meloda.fast.model.api.domain.VkMessage +import kotlinx.coroutines.flow.MutableStateFlow + +internal class MessagesHistoryInteractionHandler( + private val screenState: MutableStateFlow, + private val messages: MutableStateFlow>, + private val dialog: MutableStateFlow, + private val selectedMessages: MutableStateFlow>, + private val messageActions: MessagesHistoryMessageActions, + private val messageTransportActions: MessagesHistoryMessageTransportActions, + private val syncUiMessages: () -> Unit +) { + fun onDialogConfirmed(dialog: MessageDialog, bundle: Bundle) { + onDialogDismissed(dialog) + + when (dialog) { + is MessageDialog.MessageOptions -> Unit + + is MessageDialog.MessageDelete -> { + val deleteForEveryone = bundle.getBoolean("everyone") + + if (dialog.message.id <= 0) { + val newMessages = messages.value.toMutableList() + newMessages.remove(dialog.message) + messages.setValue { newMessages } + syncUiMessages() + return + } + + messageTransportActions.deleteMessage( + messageIds = listOf(dialog.message.id), + deleteForAll = deleteForEveryone + ) + } + + is MessageDialog.MessagesDelete -> { + val deleteForEveryone = bundle.getBoolean("everyone") + + val failedMessages = dialog.messages.filter { it.id <= 0 } + val messageIdsToDelete = + dialog.messages + .filter { it.id > 0 } + .map(VkMessage::id) + + messageTransportActions.deleteMessage( + messageIds = messageIdsToDelete, + deleteForAll = deleteForEveryone, + onSuccess = { + val newMessages = messages.value.toMutableList() + newMessages.removeAll(failedMessages) + messages.setValue { newMessages } + selectedMessages.setValue { emptyList() } + syncUiMessages() + } + ) + } + + is MessageDialog.MessagePin -> { + messageTransportActions.pinMessage(dialog.messageId) + } + + is MessageDialog.MessageUnpin -> { + messageTransportActions.unpinMessage(dialog.messageId) + } + + is MessageDialog.MessageMarkImportance -> { + messageTransportActions.markAsImportant( + messageIds = listOf(dialog.message.id), + important = dialog.isImportant + ) + } + + is MessageDialog.MessageSpam -> { + if (dialog.isSpam) { + messageTransportActions.deleteMessage( + messageIds = listOf(dialog.message.id), + spam = true + ) + } else { + // TODO: 29-Mar-25, Danil Nikolaev: report as not spam + } + } + } + } + + fun onDialogDismissed(dialog: MessageDialog) { + this.dialog.setValue { null } + } + + fun onDialogItemPicked(dialog: MessageDialog, bundle: Bundle) { + when (dialog) { + is MessageDialog.MessageOptions -> { + val cmId = bundle.getLong("cmId") + + when (val option = bundle.getParcelableCompat("option", MessageOption::class)) { + null -> Unit + + MessageOption.Retry -> { + // TODO: 28-Mar-25, Danil Nikolaev: retry sending + } + + MessageOption.Reply -> messageActions.replyToMessage(cmId) + + MessageOption.ForwardHere -> { + + } + + MessageOption.Forward -> { + + } + + MessageOption.Pin -> { + this.dialog.setValue { + MessageDialog.MessagePin(dialog.message.id) + } + } + + MessageOption.Unpin -> { + this.dialog.setValue { + MessageDialog.MessageUnpin(dialog.message.id) + } + } + + MessageOption.Read -> { + messageTransportActions.readMessage(dialog.message) + } + + MessageOption.Copy -> { + messageTransportActions.copyMessage(dialog.message) + } + + MessageOption.MarkAsImportant, + MessageOption.UnmarkAsImportant -> { + this.dialog.setValue { + MessageDialog.MessageMarkImportance( + message = dialog.message, + isImportant = option is MessageOption.MarkAsImportant + ) + } + } + + MessageOption.MarkAsSpam, + MessageOption.UnmarkAsSpam -> { + this.dialog.setValue { + MessageDialog.MessageSpam( + message = dialog.message, + isSpam = option is MessageOption.MarkAsSpam + ) + } + } + + MessageOption.Edit -> { + messageActions.editMessage(cmId) + syncUiMessages() + } + + MessageOption.Delete -> { + this.dialog.setValue { + MessageDialog.MessageDelete(dialog.message) + } + } + } + } + + is MessageDialog.MessageDelete -> Unit + is MessageDialog.MessageUnpin -> Unit + is MessageDialog.MessageMarkImportance -> Unit + is MessageDialog.MessageSpam -> Unit + is MessageDialog.MessagePin -> Unit + is MessageDialog.MessagesDelete -> Unit + } + } + + fun onCloseButtonClicked() { + if (selectedMessages.value.isNotEmpty()) { + selectedMessages.setValue { emptyList() } + } + + if (screenState.value.editCmId != null) { + messageActions.stopEditMessage() + } + + syncUiMessages() + } + + fun onMessageClicked(messageId: Long) { + val currentMessage = messages.value.firstOrNull { it.id == messageId } ?: return + + if (selectedMessages.value.isNotEmpty()) { + val isSelected = selectedMessages.value.contains(currentMessage) + + selectedMessages.setValue { old -> + old.toMutableList().also { + if (isSelected) { + it.remove(currentMessage) + } else { + it.add(currentMessage) + } + } + } + syncUiMessages() + } else { + dialog.setValue { + MessageDialog.MessageOptions(currentMessage) + } + } + } + + fun onMessageLongClicked(messageId: Long) { + val currentMessage = messages.value.firstOrNull { it.id == messageId } ?: return + + val isSelected = selectedMessages.value.contains(currentMessage) + if (isSelected) return + + selectedMessages.setValue { old -> + old.toMutableList().also { + it.add(currentMessage) + } + } + syncUiMessages() + } + + fun onEditSelectedMessageClicked() { + val cmId = selectedMessages.value.firstOrNull()?.cmId ?: return + + selectedMessages.setValue { emptyList() } + + messageActions.editMessage(cmId) + + syncUiMessages() + } + + fun onDeleteSelectedMessagesClicked() { + dialog.setValue { + MessageDialog.MessagesDelete(selectedMessages.value) + } + } +} diff --git a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryViewModelImpl.kt b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryViewModelImpl.kt index 457e27d7..e55bff3f 100644 --- a/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryViewModelImpl.kt +++ b/feature/messageshistory/src/main/kotlin/dev/meloda/fast/messageshistory/MessagesHistoryViewModelImpl.kt @@ -8,7 +8,6 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.conena.nanokt.collections.indexOfFirstOrNull -import dev.meloda.fast.common.extensions.getParcelableCompat import dev.meloda.fast.common.extensions.listenValue import dev.meloda.fast.common.extensions.setValue import dev.meloda.fast.common.provider.ResourceProvider @@ -21,7 +20,6 @@ import dev.meloda.fast.domain.MessagesUseCase import dev.meloda.fast.messageshistory.model.ActionMode import dev.meloda.fast.messageshistory.model.MessageDialog import dev.meloda.fast.messageshistory.model.MessageNavigation -import dev.meloda.fast.messageshistory.model.MessageOption import dev.meloda.fast.messageshistory.model.MessagesHistoryScreenState import dev.meloda.fast.messageshistory.navigation.MessagesHistory import dev.meloda.fast.model.BaseError @@ -116,6 +114,16 @@ class MessagesHistoryViewModelImpl( onPinnedMessageChanged = pinnedMessageHandler::update ) + private val interactionHandler = MessagesHistoryInteractionHandler( + screenState = screenState, + messages = messages, + dialog = dialog, + selectedMessages = selectedMessages, + messageActions = messageActions, + messageTransportActions = messageTransportActions, + syncUiMessages = ::syncUiMessages + ) + init { val arguments = MessagesHistory.from(savedStateHandle).arguments @@ -150,183 +158,21 @@ class MessagesHistoryViewModelImpl( } } - override fun onDialogConfirmed(dialog: MessageDialog, bundle: Bundle) { - onDialogDismissed(dialog) + override fun onDialogConfirmed(dialog: MessageDialog, bundle: Bundle) = + interactionHandler.onDialogConfirmed(dialog, bundle) - when (dialog) { - is MessageDialog.MessageOptions -> Unit + override fun onDialogDismissed(dialog: MessageDialog) = + interactionHandler.onDialogDismissed(dialog) - is MessageDialog.MessageDelete -> { - val deleteForEveryone = bundle.getBoolean("everyone") - - if (dialog.message.id <= 0) { - val newMessages = messages.value.toMutableList() - newMessages.remove(dialog.message) - messages.setValue { newMessages } - syncUiMessages() - return - } - - messageTransportActions.deleteMessage( - messageIds = listOf(dialog.message.id), - deleteForAll = deleteForEveryone - ) - } - - is MessageDialog.MessagesDelete -> { - val deleteForEveryone = bundle.getBoolean("everyone") - - val failedMessages = dialog.messages.filter { it.id <= 0 } - val messageIdsToDelete = - dialog.messages - .filter { it.id > 0 } - .map(VkMessage::id) - - messageTransportActions.deleteMessage( - messageIds = messageIdsToDelete, - deleteForAll = deleteForEveryone, - onSuccess = { - val newMessages = messages.value.toMutableList() - newMessages.removeAll(failedMessages) - messages.setValue { newMessages } - selectedMessages.setValue { emptyList() } - syncUiMessages() - } - ) - } - - is MessageDialog.MessagePin -> { - messageTransportActions.pinMessage(dialog.messageId) - } - - is MessageDialog.MessageUnpin -> { - messageTransportActions.unpinMessage(dialog.messageId) - } - - is MessageDialog.MessageMarkImportance -> { - messageTransportActions.markAsImportant( - messageIds = listOf(dialog.message.id), - important = dialog.isImportant - ) - } - - is MessageDialog.MessageSpam -> { - if (dialog.isSpam) { - messageTransportActions.deleteMessage( - messageIds = listOf(dialog.message.id), - spam = true - ) - } else { - // TODO: 29-Mar-25, Danil Nikolaev: report as not spam - } - } - } - } - - override fun onDialogDismissed(dialog: MessageDialog) { - this.dialog.setValue { null } - } - - override fun onDialogItemPicked(dialog: MessageDialog, bundle: Bundle) { - when (dialog) { - is MessageDialog.MessageOptions -> { -// val messageId = bundle.getLong("messageId") - val cmId = bundle.getLong("cmId") - - when (val option = bundle.getParcelableCompat("option", MessageOption::class)) { - null -> Unit - - MessageOption.Retry -> { - // TODO: 28-Mar-25, Danil Nikolaev: retry sending - } - - MessageOption.Reply -> messageActions.replyToMessage(cmId) - - MessageOption.ForwardHere -> { - - } - - MessageOption.Forward -> { - - } - - MessageOption.Pin -> { - this.dialog.setValue { - MessageDialog.MessagePin(dialog.message.id) - } - } - - MessageOption.Unpin -> { - this.dialog.setValue { - MessageDialog.MessageUnpin(dialog.message.id) - } - } - - MessageOption.Read -> { - messageTransportActions.readMessage(dialog.message) - } - - MessageOption.Copy -> { - messageTransportActions.copyMessage(dialog.message) - } - - MessageOption.MarkAsImportant, - MessageOption.UnmarkAsImportant -> { - this.dialog.setValue { - MessageDialog.MessageMarkImportance( - message = dialog.message, - isImportant = option is MessageOption.MarkAsImportant - ) - } - } - - MessageOption.MarkAsSpam, - MessageOption.UnmarkAsSpam -> { - this.dialog.setValue { - MessageDialog.MessageSpam( - message = dialog.message, - isSpam = option is MessageOption.MarkAsSpam - ) - } - } - - MessageOption.Edit -> { - messageActions.editMessage(cmId) - syncUiMessages() - } - - MessageOption.Delete -> { - this.dialog.setValue { - MessageDialog.MessageDelete(dialog.message) - } - } - } - } - - is MessageDialog.MessageDelete -> Unit - is MessageDialog.MessageUnpin -> Unit - is MessageDialog.MessageMarkImportance -> Unit - is MessageDialog.MessageSpam -> Unit - is MessageDialog.MessagePin -> Unit - is MessageDialog.MessagesDelete -> Unit - } - } + override fun onDialogItemPicked(dialog: MessageDialog, bundle: Bundle) = + interactionHandler.onDialogItemPicked(dialog, bundle) override fun onScrolledToIndex() { isNeedToScrollToIndex.setValue { null } } - override fun onCloseButtonClicked() { - if (selectedMessages.value.isNotEmpty()) { - selectedMessages.setValue { emptyList() } - } - - if (screenState.value.editCmId != null) { - messageActions.stopEditMessage() - } - - syncUiMessages() - } + override fun onCloseButtonClicked() = + interactionHandler.onCloseButtonClicked() override fun onRefresh() { loaders.loadMessagesHistory(offset = 0) @@ -391,42 +237,11 @@ class MessagesHistoryViewModelImpl( loaders.loadMessagesHistory(currentOffset.value) } - override fun onMessageClicked(messageId: Long) { - val currentMessage = messages.value.firstOrNull { it.id == messageId } ?: return + override fun onMessageClicked(messageId: Long) = + interactionHandler.onMessageClicked(messageId) - if (selectedMessages.value.isNotEmpty()) { - val isSelected = selectedMessages.value.contains(currentMessage) - - selectedMessages.setValue { old -> - old.toMutableList().also { - if (isSelected) { - it.remove(currentMessage) - } else { - it.add(currentMessage) - } - } - } - syncUiMessages() - } else { - dialog.setValue { - MessageDialog.MessageOptions(currentMessage) - } - } - } - - override fun onMessageLongClicked(messageId: Long) { - val currentMessage = messages.value.firstOrNull { it.id == messageId } ?: return - - val isSelected = selectedMessages.value.contains(currentMessage) - if (isSelected) return - - selectedMessages.setValue { old -> - old.toMutableList().also { - it.add(currentMessage) - } - } - syncUiMessages() - } + override fun onMessageLongClicked(messageId: Long) = + interactionHandler.onMessageLongClicked(messageId) override fun onPinnedMessageClicked(messageId: Long) { val uiMessages = uiMessages.value @@ -448,21 +263,11 @@ class MessagesHistoryViewModelImpl( } } - override fun onEditSelectedMessageClicked() { - val cmId = selectedMessages.value.firstOrNull()?.cmId ?: return + override fun onEditSelectedMessageClicked() = + interactionHandler.onEditSelectedMessageClicked() - selectedMessages.setValue { emptyList() } - - messageActions.editMessage(cmId) - - syncUiMessages() - } - - override fun onDeleteSelectedMessagesClicked() { - dialog.setValue { - MessageDialog.MessagesDelete(selectedMessages.value) - } - } + override fun onDeleteSelectedMessagesClicked() = + interactionHandler.onDeleteSelectedMessagesClicked() override fun onBoldClicked() { messageActions.onBoldClicked()