diff --git a/core/common/src/main/kotlin/dev/meloda/fast/common/paging/Paging.kt b/core/common/src/main/kotlin/dev/meloda/fast/common/paging/Paging.kt new file mode 100644 index 00000000..8f738724 --- /dev/null +++ b/core/common/src/main/kotlin/dev/meloda/fast/common/paging/Paging.kt @@ -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 mergePage( + existing: List, + page: List, + offset: Int +): List = 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 +) diff --git a/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/ChatMaterialsViewModel.kt b/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/ChatMaterialsViewModel.kt index ebc96c27..d8191af2 100644 --- a/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/ChatMaterialsViewModel.kt +++ b/feature/chatmaterials/src/main/kotlin/dev/meloda/fast/chatmaterials/ChatMaterialsViewModel.kt @@ -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 ) } } diff --git a/feature/convos/src/main/kotlin/dev/meloda/fast/convos/ConvosViewModel.kt b/feature/convos/src/main/kotlin/dev/meloda/fast/convos/ConvosViewModel.kt index 57b00db4..1606c756 100644 --- a/feature/convos/src/main/kotlin/dev/meloda/fast/convos/ConvosViewModel.kt +++ b/feature/convos/src/main/kotlin/dev/meloda/fast/convos/ConvosViewModel.kt @@ -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 ) } } diff --git a/feature/createchat/src/main/kotlin/dev/meloda/fast/convos/CreateChatViewModel.kt b/feature/createchat/src/main/kotlin/dev/meloda/fast/convos/CreateChatViewModel.kt index 9404e2e2..acde765a 100644 --- a/feature/createchat/src/main/kotlin/dev/meloda/fast/convos/CreateChatViewModel.kt +++ b/feature/createchat/src/main/kotlin/dev/meloda/fast/convos/CreateChatViewModel.kt @@ -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 ) } } diff --git a/feature/friends/src/main/kotlin/dev/meloda/fast/friends/FriendsViewModel.kt b/feature/friends/src/main/kotlin/dev/meloda/fast/friends/FriendsViewModel.kt index b953f886..af342b38 100644 --- a/feature/friends/src/main/kotlin/dev/meloda/fast/friends/FriendsViewModel.kt +++ b/feature/friends/src/main/kotlin/dev/meloda/fast/friends/FriendsViewModel.kt @@ -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 ) } } 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 c3440949..e6c1542b 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 @@ -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 ) } }