refactor: extract paging helpers

This commit is contained in:
Codex
2026-05-14 18:01:38 +03:00
parent d91b726b9d
commit 2e472733d9
6 changed files with 114 additions and 75 deletions
@@ -9,6 +9,10 @@ import dev.meloda.fast.chatmaterials.navigation.ChatMaterials
import dev.meloda.fast.chatmaterials.util.asPresentation
import dev.meloda.fast.common.extensions.listenValue
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.data.State
import dev.meloda.fast.data.VkUtils
import dev.meloda.fast.data.processState
@@ -89,11 +93,14 @@ class ChatMaterialsViewModelImpl(
state.processState(
error = ::handleError,
success = { response ->
val itemsCountSufficient = response.size == LOAD_COUNT
val itemsCountSufficient = canPaginatePage(LOAD_COUNT, response.size)
canPaginate.setValue { itemsCountSufficient }
val paginationExhausted = !itemsCountSufficient
&& screenState.value.materials.isNotEmpty()
val paginationExhausted = isPaginationExhaustedPage(
pageSize = LOAD_COUNT,
loadedCount = response.size,
hasExistingItems = screenState.value.materials.isNotEmpty()
)
val loadedMaterials = response.mapNotNull(VkAttachmentHistoryMessage::asPresentation)
@@ -107,24 +114,19 @@ class ChatMaterialsViewModelImpl(
}
)
if (offset == 0) {
screenState.setValue {
newState.copy(materials = loadedMaterials)
}
} else {
screenState.setValue {
newState.copy(
materials = newState.materials.plus(loadedMaterials)
)
}
screenState.setValue {
newState.copy(
materials = mergePage(newState.materials, loadedMaterials, offset)
)
}
}
)
val flags = loadingFlags(offset, state.isLoading())
screenState.setValue { old ->
old.copy(
isLoading = offset == 0 && state.isLoading(),
isPaginating = offset > 0 && state.isLoading()
isLoading = flags.isLoading,
isPaginating = flags.isPaginating
)
}
}
@@ -14,6 +14,10 @@ import dev.meloda.fast.common.extensions.findWithIndex
import dev.meloda.fast.common.extensions.listenValue
import dev.meloda.fast.common.extensions.setValue
import dev.meloda.fast.common.extensions.updateValue
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.convos.model.ConvoDialog
import dev.meloda.fast.convos.model.ConvoNavigation
import dev.meloda.fast.convos.model.ConvosScreenState
@@ -264,17 +268,12 @@ class ConvosViewModel(
_baseError.update { newBaseError }
},
success = { response ->
val convos = response
val fullConvos = if (offset == 0) {
convos
} else {
this.convos.value.plus(convos)
}
val itemsCountSufficient = response.size == LOAD_COUNT
val paginationExhausted = !itemsCountSufficient &&
this.convos.value.isNotEmpty()
val itemsCountSufficient = canPaginatePage(LOAD_COUNT, response.size)
val paginationExhausted = isPaginationExhaustedPage(
pageSize = LOAD_COUNT,
loadedCount = response.size,
hasExistingItems = this.convos.value.isNotEmpty()
)
_screenState.updateValue {
copy(isPaginationExhausted = paginationExhausted)
@@ -293,16 +292,17 @@ class ConvosViewModel(
convoUseCase.storeConvos(response)
_convos.emit(fullConvos)
_convos.emit(mergePage(this.convos.value, response, offset))
syncUiConvos()
_canPaginate.setValue { itemsCountSufficient }
}
)
val flags = loadingFlags(offset, state.isLoading())
_screenState.setValue { old ->
old.copy(
isLoading = offset == 0 && state.isLoading(),
isPaginating = offset > 0 && state.isLoading()
isLoading = flags.isLoading,
isPaginating = flags.isPaginating
)
}
}
@@ -7,6 +7,10 @@ import coil.ImageLoader
import coil.request.ImageRequest
import dev.meloda.fast.common.extensions.listenValue
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.convos.model.CreateChatScreenState
import dev.meloda.fast.data.State
import dev.meloda.fast.data.UserConfig
@@ -154,13 +158,16 @@ class CreateChatViewModel(
friendsUseCase.getFriends(count = LOAD_COUNT, offset = offset)
.listenValue(viewModelScope) { state ->
state.processState(
error = ::handleError,
success = { response ->
val itemsCountSufficient = response.size == LOAD_COUNT
_canPaginate.setValue { itemsCountSufficient }
error = ::handleError,
success = { response ->
val itemsCountSufficient = canPaginatePage(LOAD_COUNT, response.size)
_canPaginate.setValue { itemsCountSufficient }
val paginationExhausted = !itemsCountSufficient &&
screenState.value.friends.isNotEmpty()
val paginationExhausted = isPaginationExhaustedPage(
pageSize = LOAD_COUNT,
loadedCount = response.size,
hasExistingItems = screenState.value.friends.isNotEmpty()
)
val imagesToPreload =
response.mapNotNull { it.photo100.takeIf { p -> !p.isNullOrEmpty() } }
@@ -182,24 +189,19 @@ class CreateChatViewModel(
val newState = screenState.value.copy(
isPaginationExhausted = paginationExhausted
)
if (offset == 0) {
_screenState.setValue {
newState.copy(friends = loadedFriends)
}
} else {
_screenState.setValue {
newState.copy(
friends = newState.friends.plus(loadedFriends)
)
}
_screenState.setValue {
newState.copy(
friends = mergePage(newState.friends, loadedFriends, offset)
)
}
}
)
val flags = loadingFlags(offset, state.isLoading())
_screenState.setValue { old ->
old.copy(
isLoading = offset == 0 && state.isLoading(),
isPaginating = offset > 0 && state.isLoading()
isLoading = flags.isLoading,
isPaginating = flags.isPaginating
)
}
}
@@ -4,6 +4,10 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dev.meloda.fast.common.extensions.listenValue
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.data.State
import dev.meloda.fast.data.VkUtils
import dev.meloda.fast.data.processState
@@ -122,11 +126,14 @@ class FriendsViewModelImpl(
state.processState(
error = ::handleError,
success = { response ->
val itemsCountSufficient = response.size == LOAD_COUNT
val itemsCountSufficient = canPaginatePage(LOAD_COUNT, response.size)
canPaginate.setValue { itemsCountSufficient }
val paginationExhausted = !itemsCountSufficient
&& screenState.value.friends.isNotEmpty()
val paginationExhausted = isPaginationExhaustedPage(
pageSize = LOAD_COUNT,
loadedCount = response.size,
hasExistingItems = screenState.value.friends.isNotEmpty()
)
imagesToPreload.setValue {
response.mapNotNull(VkUser::photo100)
@@ -142,24 +149,20 @@ class FriendsViewModelImpl(
isPaginationExhausted = paginationExhausted
)
if (offset == 0) {
friends.emit(response)
screenState.setValue {
newState.copy(friends = loadedFriends)
}
} else {
friends.emit(friends.value.plus(response))
screenState.setValue {
newState.copy(friends = newState.friends.plus(loadedFriends))
}
friends.emit(mergePage(friends.value, response, offset))
screenState.setValue {
newState.copy(
friends = mergePage(newState.friends, loadedFriends, offset)
)
}
}
)
val flags = loadingFlags(offset, state.isLoading())
screenState.setValue { old ->
old.copy(
isLoading = offset == 0 && state.isLoading(),
isPaginating = offset > 0 && state.isLoading()
isLoading = flags.isLoading,
isPaginating = flags.isPaginating
)
}
}
@@ -197,10 +200,14 @@ class OnlineFriendsViewModelImpl(
}
)
val flags = loadingFlags(
offset = offset,
isLoading = onlineState.isLoading() || state.isLoading()
)
screenState.setValue { old ->
old.copy(
isLoading = offset == 0 && (onlineState.isLoading() || state.isLoading()),
isPaginating = offset > 0 && (onlineState.isLoading() || state.isLoading())
isLoading = flags.isLoading,
isPaginating = flags.isPaginating
)
}
}
@@ -31,6 +31,10 @@ import dev.meloda.fast.common.extensions.orDots
import dev.meloda.fast.common.extensions.removeIfCompat
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.UserConfig
import dev.meloda.fast.data.VkUtils
@@ -915,11 +919,7 @@ class MessagesHistoryViewModelImpl(
error = ::handleError,
success = { response ->
val messages = response.messages
val fullMessages = if (offset == 0) {
messages
} else {
this.messages.value.plus(messages)
}.sorted()
val fullMessages = mergePage(this.messages.value, messages, offset).sorted()
val convos = response.convos
@@ -930,10 +930,12 @@ class MessagesHistoryViewModelImpl(
messagesUseCase.storeMessages(messages)
convoUseCase.storeConvos(convos)
val itemsCountSufficient = messages.size == MESSAGES_LOAD_COUNT
val paginationExhausted = !itemsCountSufficient &&
this.messages.value.isNotEmpty()
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)
}
@@ -944,10 +946,11 @@ class MessagesHistoryViewModelImpl(
}
)
val flags = loadingFlags(offset, state.isLoading())
screenState.setValue { old ->
old.copy(
isLoading = offset == 0 && state.isLoading(),
isPaginating = offset > 0 && state.isLoading()
isLoading = flags.isLoading,
isPaginating = flags.isPaginating
)
}
}