forked from melod1n/fast-messenger
refactor: extract paging helpers
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
package dev.meloda.fast.common.paging
|
||||||
|
|
||||||
|
fun canPaginate(pageSize: Int, loadedCount: Int): Boolean = loadedCount == pageSize
|
||||||
|
|
||||||
|
fun isPaginationExhausted(
|
||||||
|
pageSize: Int,
|
||||||
|
loadedCount: Int,
|
||||||
|
hasExistingItems: Boolean
|
||||||
|
): Boolean = loadedCount != pageSize && hasExistingItems
|
||||||
|
|
||||||
|
fun <T> mergePage(
|
||||||
|
existing: List<T>,
|
||||||
|
page: List<T>,
|
||||||
|
offset: Int
|
||||||
|
): List<T> = if (offset == 0) page else existing + page
|
||||||
|
|
||||||
|
data class LoadingFlags(
|
||||||
|
val isLoading: Boolean,
|
||||||
|
val isPaginating: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
fun loadingFlags(offset: Int, isLoading: Boolean): LoadingFlags = LoadingFlags(
|
||||||
|
isLoading = offset == 0 && isLoading,
|
||||||
|
isPaginating = offset > 0 && isLoading
|
||||||
|
)
|
||||||
+14
-12
@@ -9,6 +9,10 @@ import dev.meloda.fast.chatmaterials.navigation.ChatMaterials
|
|||||||
import dev.meloda.fast.chatmaterials.util.asPresentation
|
import dev.meloda.fast.chatmaterials.util.asPresentation
|
||||||
import dev.meloda.fast.common.extensions.listenValue
|
import dev.meloda.fast.common.extensions.listenValue
|
||||||
import dev.meloda.fast.common.extensions.setValue
|
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.State
|
||||||
import dev.meloda.fast.data.VkUtils
|
import dev.meloda.fast.data.VkUtils
|
||||||
import dev.meloda.fast.data.processState
|
import dev.meloda.fast.data.processState
|
||||||
@@ -89,11 +93,14 @@ class ChatMaterialsViewModelImpl(
|
|||||||
state.processState(
|
state.processState(
|
||||||
error = ::handleError,
|
error = ::handleError,
|
||||||
success = { response ->
|
success = { response ->
|
||||||
val itemsCountSufficient = response.size == LOAD_COUNT
|
val itemsCountSufficient = canPaginatePage(LOAD_COUNT, response.size)
|
||||||
canPaginate.setValue { itemsCountSufficient }
|
canPaginate.setValue { itemsCountSufficient }
|
||||||
|
|
||||||
val paginationExhausted = !itemsCountSufficient
|
val paginationExhausted = isPaginationExhaustedPage(
|
||||||
&& screenState.value.materials.isNotEmpty()
|
pageSize = LOAD_COUNT,
|
||||||
|
loadedCount = response.size,
|
||||||
|
hasExistingItems = screenState.value.materials.isNotEmpty()
|
||||||
|
)
|
||||||
|
|
||||||
val loadedMaterials = response.mapNotNull(VkAttachmentHistoryMessage::asPresentation)
|
val loadedMaterials = response.mapNotNull(VkAttachmentHistoryMessage::asPresentation)
|
||||||
|
|
||||||
@@ -107,24 +114,19 @@ class ChatMaterialsViewModelImpl(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (offset == 0) {
|
|
||||||
screenState.setValue {
|
|
||||||
newState.copy(materials = loadedMaterials)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
screenState.setValue {
|
screenState.setValue {
|
||||||
newState.copy(
|
newState.copy(
|
||||||
materials = newState.materials.plus(loadedMaterials)
|
materials = mergePage(newState.materials, loadedMaterials, offset)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val flags = loadingFlags(offset, state.isLoading())
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
isLoading = offset == 0 && state.isLoading(),
|
isLoading = flags.isLoading,
|
||||||
isPaginating = offset > 0 && state.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.listenValue
|
||||||
import dev.meloda.fast.common.extensions.setValue
|
import dev.meloda.fast.common.extensions.setValue
|
||||||
import dev.meloda.fast.common.extensions.updateValue
|
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.ConvoDialog
|
||||||
import dev.meloda.fast.convos.model.ConvoNavigation
|
import dev.meloda.fast.convos.model.ConvoNavigation
|
||||||
import dev.meloda.fast.convos.model.ConvosScreenState
|
import dev.meloda.fast.convos.model.ConvosScreenState
|
||||||
@@ -264,17 +268,12 @@ class ConvosViewModel(
|
|||||||
_baseError.update { newBaseError }
|
_baseError.update { newBaseError }
|
||||||
},
|
},
|
||||||
success = { response ->
|
success = { response ->
|
||||||
val convos = response
|
val itemsCountSufficient = canPaginatePage(LOAD_COUNT, response.size)
|
||||||
val fullConvos = if (offset == 0) {
|
val paginationExhausted = isPaginationExhaustedPage(
|
||||||
convos
|
pageSize = LOAD_COUNT,
|
||||||
} else {
|
loadedCount = response.size,
|
||||||
this.convos.value.plus(convos)
|
hasExistingItems = this.convos.value.isNotEmpty()
|
||||||
}
|
)
|
||||||
|
|
||||||
val itemsCountSufficient = response.size == LOAD_COUNT
|
|
||||||
|
|
||||||
val paginationExhausted = !itemsCountSufficient &&
|
|
||||||
this.convos.value.isNotEmpty()
|
|
||||||
|
|
||||||
_screenState.updateValue {
|
_screenState.updateValue {
|
||||||
copy(isPaginationExhausted = paginationExhausted)
|
copy(isPaginationExhausted = paginationExhausted)
|
||||||
@@ -293,16 +292,17 @@ class ConvosViewModel(
|
|||||||
|
|
||||||
convoUseCase.storeConvos(response)
|
convoUseCase.storeConvos(response)
|
||||||
|
|
||||||
_convos.emit(fullConvos)
|
_convos.emit(mergePage(this.convos.value, response, offset))
|
||||||
syncUiConvos()
|
syncUiConvos()
|
||||||
_canPaginate.setValue { itemsCountSufficient }
|
_canPaginate.setValue { itemsCountSufficient }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val flags = loadingFlags(offset, state.isLoading())
|
||||||
_screenState.setValue { old ->
|
_screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
isLoading = offset == 0 && state.isLoading(),
|
isLoading = flags.isLoading,
|
||||||
isPaginating = offset > 0 && state.isLoading()
|
isPaginating = flags.isPaginating
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,10 @@ import coil.ImageLoader
|
|||||||
import coil.request.ImageRequest
|
import coil.request.ImageRequest
|
||||||
import dev.meloda.fast.common.extensions.listenValue
|
import dev.meloda.fast.common.extensions.listenValue
|
||||||
import dev.meloda.fast.common.extensions.setValue
|
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.convos.model.CreateChatScreenState
|
||||||
import dev.meloda.fast.data.State
|
import dev.meloda.fast.data.State
|
||||||
import dev.meloda.fast.data.UserConfig
|
import dev.meloda.fast.data.UserConfig
|
||||||
@@ -156,11 +160,14 @@ class CreateChatViewModel(
|
|||||||
state.processState(
|
state.processState(
|
||||||
error = ::handleError,
|
error = ::handleError,
|
||||||
success = { response ->
|
success = { response ->
|
||||||
val itemsCountSufficient = response.size == LOAD_COUNT
|
val itemsCountSufficient = canPaginatePage(LOAD_COUNT, response.size)
|
||||||
_canPaginate.setValue { itemsCountSufficient }
|
_canPaginate.setValue { itemsCountSufficient }
|
||||||
|
|
||||||
val paginationExhausted = !itemsCountSufficient &&
|
val paginationExhausted = isPaginationExhaustedPage(
|
||||||
screenState.value.friends.isNotEmpty()
|
pageSize = LOAD_COUNT,
|
||||||
|
loadedCount = response.size,
|
||||||
|
hasExistingItems = screenState.value.friends.isNotEmpty()
|
||||||
|
)
|
||||||
|
|
||||||
val imagesToPreload =
|
val imagesToPreload =
|
||||||
response.mapNotNull { it.photo100.takeIf { p -> !p.isNullOrEmpty() } }
|
response.mapNotNull { it.photo100.takeIf { p -> !p.isNullOrEmpty() } }
|
||||||
@@ -182,24 +189,19 @@ class CreateChatViewModel(
|
|||||||
val newState = screenState.value.copy(
|
val newState = screenState.value.copy(
|
||||||
isPaginationExhausted = paginationExhausted
|
isPaginationExhausted = paginationExhausted
|
||||||
)
|
)
|
||||||
if (offset == 0) {
|
|
||||||
_screenState.setValue {
|
|
||||||
newState.copy(friends = loadedFriends)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_screenState.setValue {
|
_screenState.setValue {
|
||||||
newState.copy(
|
newState.copy(
|
||||||
friends = newState.friends.plus(loadedFriends)
|
friends = mergePage(newState.friends, loadedFriends, offset)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val flags = loadingFlags(offset, state.isLoading())
|
||||||
_screenState.setValue { old ->
|
_screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
isLoading = offset == 0 && state.isLoading(),
|
isLoading = flags.isLoading,
|
||||||
isPaginating = offset > 0 && state.isLoading()
|
isPaginating = flags.isPaginating
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,10 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dev.meloda.fast.common.extensions.listenValue
|
import dev.meloda.fast.common.extensions.listenValue
|
||||||
import dev.meloda.fast.common.extensions.setValue
|
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.State
|
||||||
import dev.meloda.fast.data.VkUtils
|
import dev.meloda.fast.data.VkUtils
|
||||||
import dev.meloda.fast.data.processState
|
import dev.meloda.fast.data.processState
|
||||||
@@ -122,11 +126,14 @@ class FriendsViewModelImpl(
|
|||||||
state.processState(
|
state.processState(
|
||||||
error = ::handleError,
|
error = ::handleError,
|
||||||
success = { response ->
|
success = { response ->
|
||||||
val itemsCountSufficient = response.size == LOAD_COUNT
|
val itemsCountSufficient = canPaginatePage(LOAD_COUNT, response.size)
|
||||||
canPaginate.setValue { itemsCountSufficient }
|
canPaginate.setValue { itemsCountSufficient }
|
||||||
|
|
||||||
val paginationExhausted = !itemsCountSufficient
|
val paginationExhausted = isPaginationExhaustedPage(
|
||||||
&& screenState.value.friends.isNotEmpty()
|
pageSize = LOAD_COUNT,
|
||||||
|
loadedCount = response.size,
|
||||||
|
hasExistingItems = screenState.value.friends.isNotEmpty()
|
||||||
|
)
|
||||||
|
|
||||||
imagesToPreload.setValue {
|
imagesToPreload.setValue {
|
||||||
response.mapNotNull(VkUser::photo100)
|
response.mapNotNull(VkUser::photo100)
|
||||||
@@ -142,24 +149,20 @@ class FriendsViewModelImpl(
|
|||||||
isPaginationExhausted = paginationExhausted
|
isPaginationExhausted = paginationExhausted
|
||||||
)
|
)
|
||||||
|
|
||||||
if (offset == 0) {
|
friends.emit(mergePage(friends.value, response, offset))
|
||||||
friends.emit(response)
|
|
||||||
screenState.setValue {
|
screenState.setValue {
|
||||||
newState.copy(friends = loadedFriends)
|
newState.copy(
|
||||||
}
|
friends = mergePage(newState.friends, loadedFriends, offset)
|
||||||
} else {
|
)
|
||||||
friends.emit(friends.value.plus(response))
|
|
||||||
screenState.setValue {
|
|
||||||
newState.copy(friends = newState.friends.plus(loadedFriends))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val flags = loadingFlags(offset, state.isLoading())
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
isLoading = offset == 0 && state.isLoading(),
|
isLoading = flags.isLoading,
|
||||||
isPaginating = offset > 0 && state.isLoading()
|
isPaginating = flags.isPaginating
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,10 +200,14 @@ class OnlineFriendsViewModelImpl(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val flags = loadingFlags(
|
||||||
|
offset = offset,
|
||||||
|
isLoading = onlineState.isLoading() || state.isLoading()
|
||||||
|
)
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
isLoading = offset == 0 && (onlineState.isLoading() || state.isLoading()),
|
isLoading = flags.isLoading,
|
||||||
isPaginating = offset > 0 && (onlineState.isLoading() || state.isLoading())
|
isPaginating = flags.isPaginating
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-11
@@ -31,6 +31,10 @@ import dev.meloda.fast.common.extensions.orDots
|
|||||||
import dev.meloda.fast.common.extensions.removeIfCompat
|
import dev.meloda.fast.common.extensions.removeIfCompat
|
||||||
import dev.meloda.fast.common.extensions.setValue
|
import dev.meloda.fast.common.extensions.setValue
|
||||||
import dev.meloda.fast.common.provider.ResourceProvider
|
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.State
|
||||||
import dev.meloda.fast.data.UserConfig
|
import dev.meloda.fast.data.UserConfig
|
||||||
import dev.meloda.fast.data.VkUtils
|
import dev.meloda.fast.data.VkUtils
|
||||||
@@ -915,11 +919,7 @@ class MessagesHistoryViewModelImpl(
|
|||||||
error = ::handleError,
|
error = ::handleError,
|
||||||
success = { response ->
|
success = { response ->
|
||||||
val messages = response.messages
|
val messages = response.messages
|
||||||
val fullMessages = if (offset == 0) {
|
val fullMessages = mergePage(this.messages.value, messages, offset).sorted()
|
||||||
messages
|
|
||||||
} else {
|
|
||||||
this.messages.value.plus(messages)
|
|
||||||
}.sorted()
|
|
||||||
|
|
||||||
val convos = response.convos
|
val convos = response.convos
|
||||||
|
|
||||||
@@ -930,10 +930,12 @@ class MessagesHistoryViewModelImpl(
|
|||||||
messagesUseCase.storeMessages(messages)
|
messagesUseCase.storeMessages(messages)
|
||||||
convoUseCase.storeConvos(convos)
|
convoUseCase.storeConvos(convos)
|
||||||
|
|
||||||
val itemsCountSufficient = messages.size == MESSAGES_LOAD_COUNT
|
val itemsCountSufficient = canPaginatePage(MESSAGES_LOAD_COUNT, messages.size)
|
||||||
|
val paginationExhausted = isPaginationExhaustedPage(
|
||||||
val paginationExhausted = !itemsCountSufficient &&
|
pageSize = MESSAGES_LOAD_COUNT,
|
||||||
this.messages.value.isNotEmpty()
|
loadedCount = messages.size,
|
||||||
|
hasExistingItems = this.messages.value.isNotEmpty()
|
||||||
|
)
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(isPaginationExhausted = paginationExhausted)
|
old.copy(isPaginationExhausted = paginationExhausted)
|
||||||
}
|
}
|
||||||
@@ -944,10 +946,11 @@ class MessagesHistoryViewModelImpl(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val flags = loadingFlags(offset, state.isLoading())
|
||||||
screenState.setValue { old ->
|
screenState.setValue { old ->
|
||||||
old.copy(
|
old.copy(
|
||||||
isLoading = offset == 0 && state.isLoading(),
|
isLoading = flags.isLoading,
|
||||||
isPaginating = offset > 0 && state.isLoading()
|
isPaginating = flags.isPaginating
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user