forked from melod1n/fast-messenger
refactor: extract message loading
This commit is contained in:
+140
@@ -0,0 +1,140 @@
|
||||
package dev.meloda.fast.messageshistory
|
||||
|
||||
import android.util.Log
|
||||
import dev.meloda.fast.common.VkConstants
|
||||
import dev.meloda.fast.common.extensions.listenValue
|
||||
import dev.meloda.fast.common.extensions.orDots
|
||||
import dev.meloda.fast.common.extensions.setValue
|
||||
import dev.meloda.fast.common.paging.canPaginate as canPaginatePage
|
||||
import dev.meloda.fast.common.paging.isPaginationExhausted as isPaginationExhaustedPage
|
||||
import dev.meloda.fast.common.paging.loadingFlags
|
||||
import dev.meloda.fast.common.paging.mergePage
|
||||
import dev.meloda.fast.common.provider.ResourceProvider
|
||||
import dev.meloda.fast.data.State
|
||||
import dev.meloda.fast.data.VkUtils
|
||||
import dev.meloda.fast.data.processState
|
||||
import dev.meloda.fast.datastore.AppSettings
|
||||
import dev.meloda.fast.domain.ConvoUseCase
|
||||
import dev.meloda.fast.domain.MessagesUseCase
|
||||
import dev.meloda.fast.domain.util.extractAvatar
|
||||
import dev.meloda.fast.domain.util.extractTitle
|
||||
import dev.meloda.fast.messageshistory.model.MessagesHistoryScreenState
|
||||
import dev.meloda.fast.model.BaseError
|
||||
import dev.meloda.fast.model.api.domain.VkMessage
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
internal class MessagesHistoryLoaders(
|
||||
private val convoUseCase: ConvoUseCase,
|
||||
private val messagesUseCase: MessagesUseCase,
|
||||
private val resourceProvider: ResourceProvider,
|
||||
private val screenState: MutableStateFlow<MessagesHistoryScreenState>,
|
||||
private val messages: MutableStateFlow<List<VkMessage>>,
|
||||
private val imagesToPreload: MutableStateFlow<List<String>>,
|
||||
private val canPaginate: MutableStateFlow<Boolean>,
|
||||
private val baseError: MutableStateFlow<BaseError?>,
|
||||
private val scope: CoroutineScope,
|
||||
private val syncUiMessages: () -> Unit,
|
||||
private val onPinnedMessage: (VkMessage?) -> Unit,
|
||||
) {
|
||||
fun loadConvo() {
|
||||
Log.d("MessagesHistoryViewModelImpl", "loadConvo()")
|
||||
|
||||
convoUseCase.getById(
|
||||
peerIds = listOf(screenState.value.convoId),
|
||||
extended = true,
|
||||
fields = VkConstants.ALL_FIELDS
|
||||
).listenValue(scope) { state ->
|
||||
state.processState(
|
||||
error = ::handleError,
|
||||
success = { response ->
|
||||
val convo = response.firstOrNull() ?: return@listenValue
|
||||
val title = convo.extractTitle(
|
||||
useContactName = AppSettings.General.useContactNames,
|
||||
resources = resourceProvider.resources
|
||||
)
|
||||
val avatar = convo.extractAvatar()
|
||||
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
convo = convo,
|
||||
title = title,
|
||||
avatar = avatar
|
||||
)
|
||||
}
|
||||
|
||||
onPinnedMessage(convo.pinnedMessage)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun loadMessagesHistory(offset: Int) {
|
||||
Log.d("MessagesHistoryViewModel", "loadMessagesHistory: $offset")
|
||||
|
||||
messagesUseCase.getMessagesHistory(
|
||||
convoId = screenState.value.convoId,
|
||||
count = MessagesHistoryViewModelImpl.MESSAGES_LOAD_COUNT,
|
||||
offset = offset,
|
||||
).listenValue(scope) { state ->
|
||||
state.processState(
|
||||
error = ::handleError,
|
||||
success = { response ->
|
||||
val messages = response.messages
|
||||
val fullMessages = mergePage(this.messages.value, messages, offset)
|
||||
.sorted()
|
||||
|
||||
imagesToPreload.setValue {
|
||||
messages.mapNotNull { it.extractAvatar().extractUrl() }
|
||||
}
|
||||
|
||||
messagesUseCase.storeMessages(messages)
|
||||
convoUseCase.storeConvos(response.convos)
|
||||
|
||||
val itemsCountSufficient = canPaginatePage(
|
||||
MessagesHistoryViewModelImpl.MESSAGES_LOAD_COUNT,
|
||||
messages.size
|
||||
)
|
||||
val paginationExhausted = isPaginationExhaustedPage(
|
||||
pageSize = MessagesHistoryViewModelImpl.MESSAGES_LOAD_COUNT,
|
||||
loadedCount = messages.size,
|
||||
hasExistingItems = this.messages.value.isNotEmpty()
|
||||
)
|
||||
screenState.setValue { old ->
|
||||
old.copy(isPaginationExhausted = paginationExhausted)
|
||||
}
|
||||
|
||||
this.messages.emit(fullMessages)
|
||||
syncUiMessages()
|
||||
canPaginate.setValue { itemsCountSufficient }
|
||||
}
|
||||
)
|
||||
|
||||
val flags = loadingFlags(offset, state.isLoading())
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
isLoading = flags.isLoading,
|
||||
isPaginating = flags.isPaginating
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleError(error: State.Error) {
|
||||
VkUtils.parseError(error)?.let { newBaseError ->
|
||||
baseError.setValue { newBaseError }
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-117
@@ -2,35 +2,23 @@ package dev.meloda.fast.messageshistory
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.conena.nanokt.collections.indexOfFirstOrNull
|
||||
import dev.meloda.fast.common.VkConstants
|
||||
import dev.meloda.fast.common.extensions.getParcelableCompat
|
||||
import dev.meloda.fast.common.extensions.listenValue
|
||||
import dev.meloda.fast.common.extensions.orDots
|
||||
import dev.meloda.fast.common.extensions.setValue
|
||||
import dev.meloda.fast.common.provider.ResourceProvider
|
||||
import dev.meloda.fast.common.paging.canPaginate as canPaginatePage
|
||||
import dev.meloda.fast.common.paging.isPaginationExhausted as isPaginationExhaustedPage
|
||||
import dev.meloda.fast.common.paging.loadingFlags
|
||||
import dev.meloda.fast.common.paging.mergePage
|
||||
import dev.meloda.fast.data.State
|
||||
import dev.meloda.fast.data.VkUtils
|
||||
import dev.meloda.fast.data.VkMemoryCache
|
||||
import dev.meloda.fast.data.processState
|
||||
import dev.meloda.fast.datastore.AppSettings
|
||||
import dev.meloda.fast.domain.ConvoUseCase
|
||||
import dev.meloda.fast.domain.GetMessageReadPeersUseCase
|
||||
import dev.meloda.fast.domain.LoadConvosByIdUseCase
|
||||
import dev.meloda.fast.domain.LongPollUpdatesParser
|
||||
import dev.meloda.fast.domain.MessagesUseCase
|
||||
import dev.meloda.fast.domain.util.extractAvatar
|
||||
import dev.meloda.fast.domain.util.extractTitle
|
||||
import dev.meloda.fast.messageshistory.model.ActionMode
|
||||
import dev.meloda.fast.messageshistory.model.MessageDialog
|
||||
import dev.meloda.fast.messageshistory.model.MessageNavigation
|
||||
@@ -52,7 +40,6 @@ class MessagesHistoryViewModelImpl(
|
||||
private val messagesUseCase: MessagesUseCase,
|
||||
private val convoUseCase: ConvoUseCase,
|
||||
private val resourceProvider: ResourceProvider,
|
||||
private val loadConvosByIdUseCase: LoadConvosByIdUseCase,
|
||||
private val getMessageReadPeersUseCase: GetMessageReadPeersUseCase,
|
||||
updatesParser: LongPollUpdatesParser,
|
||||
savedStateHandle: SavedStateHandle
|
||||
@@ -91,6 +78,20 @@ class MessagesHistoryViewModelImpl(
|
||||
onPinnedMessageChanged = ::handlePinnedMessage
|
||||
)
|
||||
|
||||
private val loaders = MessagesHistoryLoaders(
|
||||
convoUseCase = convoUseCase,
|
||||
messagesUseCase = messagesUseCase,
|
||||
resourceProvider = resourceProvider,
|
||||
screenState = screenState,
|
||||
messages = messages,
|
||||
imagesToPreload = imagesToPreload,
|
||||
canPaginate = canPaginate,
|
||||
baseError = baseError,
|
||||
scope = viewModelScope,
|
||||
syncUiMessages = ::syncUiMessages,
|
||||
onPinnedMessage = ::handlePinnedMessage
|
||||
)
|
||||
|
||||
private val longPollEventHandler = MessagesHistoryLongPollEventHandler(
|
||||
screenState = screenState,
|
||||
messages = messages
|
||||
@@ -103,8 +104,8 @@ class MessagesHistoryViewModelImpl(
|
||||
|
||||
screenState.setValue { old -> old.copy(convoId = arguments.convoId) }
|
||||
|
||||
loadConvo()
|
||||
loadMessagesHistory()
|
||||
loaders.loadConvo()
|
||||
loaders.loadMessagesHistory(currentOffset.value)
|
||||
|
||||
updatesParser.onNewMessage(longPollEventHandler::onNewMessage)
|
||||
updatesParser.onMessageEdited(longPollEventHandler::onMessageEdited)
|
||||
@@ -311,7 +312,7 @@ class MessagesHistoryViewModelImpl(
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
loadMessagesHistory(offset = 0)
|
||||
loaders.loadMessagesHistory(offset = 0)
|
||||
}
|
||||
|
||||
override fun onAttachmentButtonClicked() {
|
||||
@@ -370,7 +371,7 @@ class MessagesHistoryViewModelImpl(
|
||||
|
||||
override fun onPaginationConditionsMet() {
|
||||
currentOffset.update { messages.value.size }
|
||||
loadMessagesHistory()
|
||||
loaders.loadMessagesHistory(currentOffset.value)
|
||||
}
|
||||
|
||||
override fun onMessageClicked(messageId: Long) {
|
||||
@@ -496,38 +497,6 @@ class MessagesHistoryViewModelImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadConvo() {
|
||||
Log.d("MessagesHistoryViewModelImpl", "loadConvo()")
|
||||
|
||||
loadConvosByIdUseCase(
|
||||
peerIds = listOf(screenState.value.convoId),
|
||||
extended = true,
|
||||
fields = VkConstants.ALL_FIELDS
|
||||
).listenValue(viewModelScope) { state ->
|
||||
state.processState(
|
||||
error = ::handleError,
|
||||
success = { response ->
|
||||
val convo = response.firstOrNull() ?: return@listenValue
|
||||
val title = convo.extractTitle(
|
||||
useContactName = AppSettings.General.useContactNames,
|
||||
resources = resourceProvider.resources
|
||||
)
|
||||
val avatar = convo.extractAvatar()
|
||||
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
convo = convo,
|
||||
title = title,
|
||||
avatar = avatar
|
||||
)
|
||||
}
|
||||
|
||||
convo.pinnedMessage?.let(::handlePinnedMessage)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePinnedMessage(pinnedMessage: VkMessage?) {
|
||||
if (pinnedMessage == null) {
|
||||
screenState.setValue { old ->
|
||||
@@ -565,73 +534,6 @@ class MessagesHistoryViewModelImpl(
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadMessagesHistory(offset: Int = currentOffset.value) {
|
||||
Log.d("MessagesHistoryViewModel", "loadMessagesHistory: $offset")
|
||||
|
||||
messagesUseCase.getMessagesHistory(
|
||||
convoId = screenState.value.convoId,
|
||||
count = MESSAGES_LOAD_COUNT,
|
||||
offset = offset,
|
||||
).listenValue(viewModelScope) { state ->
|
||||
state.processState(
|
||||
error = ::handleError,
|
||||
success = { response ->
|
||||
val messages = response.messages
|
||||
val fullMessages = mergePage(this.messages.value, messages, offset).sorted()
|
||||
|
||||
val convos = response.convos
|
||||
|
||||
imagesToPreload.setValue {
|
||||
messages.mapNotNull { it.extractAvatar().extractUrl() }
|
||||
}
|
||||
|
||||
messagesUseCase.storeMessages(messages)
|
||||
convoUseCase.storeConvos(convos)
|
||||
|
||||
val itemsCountSufficient = canPaginatePage(MESSAGES_LOAD_COUNT, messages.size)
|
||||
val paginationExhausted = isPaginationExhaustedPage(
|
||||
pageSize = MESSAGES_LOAD_COUNT,
|
||||
loadedCount = messages.size,
|
||||
hasExistingItems = this.messages.value.isNotEmpty()
|
||||
)
|
||||
screenState.setValue { old ->
|
||||
old.copy(isPaginationExhausted = paginationExhausted)
|
||||
}
|
||||
|
||||
this.messages.emit(fullMessages)
|
||||
syncUiMessages()
|
||||
canPaginate.setValue { itemsCountSufficient }
|
||||
}
|
||||
)
|
||||
|
||||
val flags = loadingFlags(offset, state.isLoading())
|
||||
screenState.setValue { old ->
|
||||
old.copy(
|
||||
isLoading = flags.isLoading,
|
||||
isPaginating = flags.isPaginating
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleError(error: State.Error) {
|
||||
VkUtils.parseError(error)?.let { newBaseError ->
|
||||
baseError.setValue { newBaseError }
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun syncUiMessages(): List<MessageUiItem> {
|
||||
val newUiMessages = buildMessagesHistoryUiMessages(
|
||||
messages = messages.value,
|
||||
|
||||
Reference in New Issue
Block a user