Coder Social home page Coder Social logo

icerockdev / moko-paging Goto Github PK

View Code? Open in Web Editor NEW
57.0 6.0 7.0 218 KB

Pagination logic in common code for mobile (android & ios) Kotlin Multiplatform development

Home Page: https://moko.icerock.dev/

License: Apache License 2.0

Kotlin 100.00%
moko kotlin-multiplatform kotlin-native ios android kotlin

moko-paging's Introduction

moko-paging
GitHub license Download kotlin-version

Mobile Kotlin paging

This is a Kotlin MultiPlatform library that contains pagination logic for kotlin multiplatform

Table of Contents

Features

  • Pagination implements pagination logic for the data from abstract PagedListDataSource.
  • Managing a data loading process using Pagination asynchronous functions: loadFirstPage, loadNextPage, refresh or their duplicates with suspend modifier.
  • Observing states of Pagination using LiveData from moko-mvvm.

Requirements

  • Gradle version 6.8+
  • Android API 16+
  • iOS version 11.0+

Installation

root build.gradle

allprojects {
    repositories {
        mavenCentral()
    }
}

project build.gradle

dependencies {
    commonMainApi("dev.icerock.moko:paging:0.7.1")
}

Usage

You can use Pagination in commonMain sourceset.

Pagination creation:

val pagination: Pagination<Int> = Pagination(
        parentScope = coroutineScope,
        dataSource = LambdaPagedListDataSource { currentList ->
            extrenalRepository.loadPage(currentList) 
        },
        comparator = Comparator { a: Int, b: Int ->
            a - b
        },
        nextPageListener = { result: Result<List<Int>> ->
            if (result.isSuccess) {
                println("Next page successful loaded")
            } else {
                println("Next page loading failed")
            }
        },
        refreshListener = { result: Result<List<Int>> ->
            if (result.isSuccess) {
                println("Refresh successful")
            } else {
                println("Refresh failed")
            }
        },
        initValue = listOf(1, 2, 3)
    )

Managing data loading:

// Loading first page
pagination.loadFirstPage()

// Loading next page
pagination.loadNextPage()

// Refreshing pagnation
pagination.refresh()

// Setting new list
pagination.setData(itemsList)

Observing Pagination states:

// Observing the state of the pagination
pagination.state.addObserver { state: ResourceState<List<ItemClass>, Throwable> -> 
    // ...
}

// Observing the next page loading process
pagination.nextPageLoading.addObserver { isLoading: Boolean -> 
    // ...
}

// Observing the refresh process
pagination.refreshLoading.addObserver { isRefreshing: Boolean -> 
    // ...    
}

Samples

Please see more examples in the sample directory.

Set Up Locally

Contributing

All development (both new features and bug fixes) is performed in the develop branch. This way master always contains the sources of the most recently released version. Please send PRs with bug fixes to the develop branch. Documentation fixes in the markdown files are an exception to this rule. They are updated directly in master.

The develop branch is pushed to master on release.

For more details on contributing please see the contributing guide.

License

Copyright 2020 IceRock MAG Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

moko-paging's People

Contributors

alex009 avatar anton6tak avatar dorofeev avatar lobynya avatar msfjarvis avatar tetraquark avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

moko-paging's Issues

Refresh logic strategy choose

pullToRefresh reset current data. Need to create choice what to do: add only intersection between old and new data or reset all current data and show only these data which place on one screen space

ArtifactNotFoundException: Could not find paging-0.4.5-samplessources.jar

Failed building KotlinMPPGradleModel
org.gradle.internal.resolve.ArtifactNotFoundException: Could not find paging-0.4.5-samplessources.jar (dev.icerock.moko:paging:0.4.5).
Searched in the following locations:
    https://repo.maven.apache.org/maven2/dev/icerock/moko/paging/0.4.5/paging-0.4.5-samplessources.jar

My usage:
shared module:

sourceSets {
        val commonMain by getting {
            dependencies {
                ...
                api("dev.icerock.moko:paging:$mokoPagingVersion")
            }
        }

Incorrect state value when setData(emptyList())

I'm trying to set a new data to pagination when clearing items.
When items list is empty, I need to set pagination state = Empty
But it's not working as I expected.
There are setDataSuspend and asStateNullIsEmpty methods:

suspend fun setDataSuspend(items: List<Item>?) {
        listMutex.lock()
        mStateStorage.value = items.asStateNullIsEmpty()
        mEndOfList.value = false
        listMutex.unlock()
    }

fun <T, E> T?.asStateNullIsEmpty() = asState {
    ResourceState.Loading<T, E>()
}

It would be great when passing emptyList to pagination we will get an Empty Resource State in it.

Publish for JVM

Looking at the code I don't see anything specific that would make this any more complex than just adding a JVM target. With compose-jb allowing code sharing between mobile and desktop for UI, being able to share UI logic like pagination using this library would be immensely helpful.

State machine for pagination logic

Here some research with states without boolean livedatas:

sealed class NewsItem {

    data class Short(
        override val id: Int,
        override val title: String
    ) : NewsItem()

    data class Detailed(
        override val id: Int,
        override val title: String,
        val text: String
    ) : NewsItem()

    abstract val id: Int
    abstract val title: String
}

interface NewsApi {
    suspend fun loadPage(page: Int, size: Int): List<NewsItem.Short>
    suspend fun loadDetails(id: Int): NewsItem.Detailed
}

class NewsRepository(
    private val api: NewsApi
) {
    private val _newsState = MutableStateFlow<ResourceStateThrow<PagingDataState<NewsItem>>>(
        value = ResourceState.Empty()
    )
    val newsState: StateFlow<ResourceStateThrow<PagingDataState<NewsItem>>> get() = _newsState

    suspend fun loadFirstPage() {
        val state = _newsState.value
        if (state !is ResourceState.Empty && state !is ResourceState.Error) return

        _newsState.value = ResourceState.Loading()
        _newsState.value = try {
            val firstPage = api.loadPage(0, PAGE_SIZE)
            val dataState = PagingDataState.Normal<NewsItem>(firstPage)
            ResourceState.Data(data = dataState)
        } catch (exc: Exception) {
            ResourceState.Error(exc)
        }
    }

    suspend fun loadNextPage() {
        val state = _newsState.value
        if (state !is ResourceState.Data) return
        if (state.data !is PagingDataState.Normal) return

        val currentItems = state.data.items
        val loadingState = PagingDataState.LoadNextPage(currentItems)
        _newsState.value = ResourceState.Data(loadingState)

        val nextPageIndex = (currentItems.size / PAGE_SIZE) + 1
        val nextPageItems = api.loadPage(nextPageIndex, PAGE_SIZE)

        val itemsToAdd = nextPageItems.filter { currentItems.contains(it).not() }

        val newState = PagingDataState.Normal(currentItems + itemsToAdd)
        _newsState.value = ResourceState.Data(newState)
    }

    suspend fun refreshData() {
        val state = _newsState.value
        if (state !is ResourceState.Data) return
        if (state.data !is PagingDataState.Normal) return

        val currentItems = state.data.items
        val loadingState = PagingDataState.Refresh(currentItems)
        _newsState.value = ResourceState.Data(loadingState)

        val updatedFirstPage = api.loadPage(0, PAGE_SIZE)

        val newState = PagingDataState.Normal<NewsItem>(updatedFirstPage)
        _newsState.value = ResourceState.Data(newState)
    }

    suspend fun loadDetails(id: Int): NewsItem.Detailed {
        val detailed = api.loadDetails(id)

        val currentState = _newsState.value
        if (currentState is ResourceState.Data && currentState.data is PagingDataState.Normal) {
            val items = currentState.data.items
            val updatedNews = items.map {
                if (it.id == id) detailed
                else it
            }
            _newsState.value = ResourceState.Data(PagingDataState.Normal(updatedNews))
        } else {
            throw IllegalStateException("try to update details item while in not normal data state")
        }

        return detailed
    }

    private companion object {
        const val PAGE_SIZE = 10
    }
}

typealias ResourceStateThrow<T> = ResourceState<T, Throwable>

sealed class ResourceState<T, E> {
    class Empty<T, E> : ResourceState<T, E>()
    class Loading<T, E> : ResourceState<T, E>()
    data class Data<T, E>(val data: T) : ResourceState<T, E>()
    data class Error<T, E>(val error: E) : ResourceState<T, E>()
}

sealed class PagingDataState<T> {
    data class Normal<T>(override val items: List<T>) : PagingDataState<T>()
    data class Refresh<T>(override val items: List<T>) : PagingDataState<T>()
    data class LoadNextPage<T>(override val items: List<T>) : PagingDataState<T>()

    abstract val items: List<T>
}

Refresh not working while loading next page

Refresh should have higher priority than loadNextPage

Actual result:
refresh will not start, if nextPageLoading is in progress.

suspend fun refreshSuspend() {        
   if (mNextPageLoading.value) return
   ...
}

Expected result:
next page loading canceled
refresh started

Fix duplicated set state

When we at end of pages and loadNextPage return empty items list - we got now duplicated set state call with equals lists.
to fix it do:

// append new items to current list
                val newList = newItems.plus(currentList)
                // mark end of list if no new items
                if (newItems.isEmpty()) {
                    mEndOfList.value = true
                } else {
                    // save
                    mStateStorage.value = newList.asState()
                }

in loadNextPage of Pagination

kotlin.native.concurrent.InvalidMutabilityException on completion of loadNextPage

Uncaught Kotlin exception: kotlinx.coroutines.CoroutinesInternalError: Fatal exception in coroutines machinery for AwaitContinuation(Shareable[used]){HttpResponseData=(statusCode=200 OK)}@1ac7d48. Please read KDoc to 'handleFatalException' method and report this incident to maintainers
    at 0   MultiPlatformLibrary                0x00000001054ee98c kfun:kotlin.Error#<init>(kotlin.String?;kotlin.Throwable?){} + 124 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/Exceptions.kt:14:63)
    at 1   MultiPlatformLibrary                0x000000010566d46c kfun:kotlinx.coroutines.CoroutinesInternalError#<init>(kotlin.String;kotlin.Throwable){} + 124 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/Exceptions.common.kt:28:77)
    at 2   MultiPlatformLibrary                0x00000001056db599 kfun:kotlinx.coroutines.DispatchedTask#handleFatalException(kotlin.Throwable?;kotlin.Throwable?){} + 953 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt:93:22)
    at 3   MultiPlatformLibrary                0x00000001056db177 kfun:kotlinx.coroutines.DispatchedTask#run(){} + 3543 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt:64:13)
    at 4   MultiPlatformLibrary                0x00000001059fa508 kfun:dev.icerock.moko.mvvm.viewmodel.UIDispatcher.dispatch$lambda-0#internal + 88 (/Users/runner/work/moko-mvvm/moko-mvvm/mvvm/src/iosMain/kotlin/dev/icerock/moko/mvvm/viewmodel/UIDispatcher.kt:17:19)
    at 5   MultiPlatformLibrary                0x00000001059faa6e kfun:dev.icerock.moko.mvvm.viewmodel.UIDispatcher.$dispatch$lambda-0$FUNCTION_REFERENCE$61.invoke#internal + 62 (/Users/runner/work/moko-mvvm/moko-mvvm/mvvm/src/iosMain/kotlin/dev/icerock/moko/mvvm/viewmodel/UIDispatcher.kt:16:32)
    at 6   MultiPlatformLibrary                0x00000001059faace kfun:dev.icerock.moko.mvvm.viewmodel.UIDispatcher.$dispatch$lambda-0$FUNCTION_REFERENCE$61.$<bridge-UNN>invoke(){}#internal + 62 (/Users/runner/work/moko-mvvm/moko-mvvm/mvvm/src/iosMain/kotlin/dev/icerock/moko/mvvm/viewmodel/UIDispatcher.kt:16:32)
    at 7   MultiPlatformLibrary                0x00000001059faee7 _6465762e696365726f636b2e6d6f6b6f3a6d76766d_knbridge68 + 183 (/Users/runner/work/moko-mvvm/moko-mvvm/mvvm/src/iosMain/kotlin/dev/icerock/moko/mvvm/viewmodel/UIDispatcher.kt:16:32)
    at 8   libdispatch.dylib                   0x0000000107c037ec _dispatch_call_block_and_release + 12
    at 9   libdispatch.dylib                   0x0000000107c049c8 _dispatch_client_callout + 8
    at 10  libdispatch.dylib                   0x0000000107c12e75 _dispatch_main_queue_callback_4CF + 1152
    at 11  CoreFoundation                      0x00007fff2038bdab __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__ + 9
    at 12  CoreFoundation                      0x00007fff2038662e __CFRunLoopRun + 2685
    at 13  CoreFoundation                      0x00007fff203856c6 CFRunLoopRunSpecific + 567
    at 14  GraphicsServices                    0x00007fff2b76adb3 GSEventRunModal + 139
    at 15  UIKitCore                           0x00007fff24675187 -[UIApplication _run] + 912
    at 16  UIKitCore                           0x00007fff2467a038 UIApplicationMain + 101
    at 17  Krohne                              0x00000001048a637b main + 75 (/Users/agusev/Desktop/projects/krohne-android/ios-app/src/AppDelegate.swift:10:7)
    at 18  libdyld.dylib                       0x00007fff20256409 start + 1
    at 19  ???                                 0x0000000000000001 0x0 + 1
Caused by: kotlin.native.concurrent.InvalidMutabilityException: mutation attempt of frozen kotlinx.coroutines.sync.MutexImpl.LockedQueue@36244e8
    at 0   MultiPlatformLibrary                0x00000001054f5ded kfun:kotlin.Throwable#<init>(kotlin.String?){} + 93 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/Throwable.kt:23:37)
    at 1   MultiPlatformLibrary                0x00000001054eea9b kfun:kotlin.Exception#<init>(kotlin.String?){} + 91 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/Exceptions.kt:23:44)
    at 2   MultiPlatformLibrary                0x00000001054eeceb kfun:kotlin.RuntimeException#<init>(kotlin.String?){} + 91 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/Exceptions.kt:34:44)
    at 3   MultiPlatformLibrary                0x000000010552777b kfun:kotlin.native.concurrent.InvalidMutabilityException#<init>(kotlin.String){} + 91 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/native/concurrent/Freezing.kt:22:60)
    at 4   MultiPlatformLibrary                0x0000000105529032 ThrowInvalidMutabilityException + 690 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/native/concurrent/Internal.kt:92:11)
    at 5   MultiPlatformLibrary                0x0000000105625ecc MutationCheck + 108
    at 6   MultiPlatformLibrary                0x00000001056f6e36 kfun:kotlinx.coroutines.sync.MutexImpl.LockedQueue.<set-owner>#internal + 102 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/sync/Mutex.kt:350:19)
    at 7   MultiPlatformLibrary                0x00000001056f60ac kfun:kotlinx.coroutines.sync.MutexImpl#unlock(kotlin.Any?){} + 3740 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/sync/Mutex.kt:327:29)
    at 8   MultiPlatformLibrary                0x00000001056f0697 kfun:kotlinx.coroutines.sync.Mutex#unlock$default(kotlin.Any?;kotlin.Int){} + 295 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/sync/Mutex.kt:89:12)
    at 9   MultiPlatformLibrary                0x0000000105a4c1e1 kfun:dev.icerock.moko.paging.Pagination.$loadNextPageSuspendCOROUTINE$1#invokeSuspend(kotlin.Result<kotlin.Any?>){}kotlin.Any? + 5553 (/Users/runner/work/moko-paging/moko-paging/paging/src/commonMain/kotlin/dev/icerock/moko/paging/Pagination.kt:111:19)
    at 10  MultiPlatformLibrary                0x0000000105517c86 kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 758 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/coroutines/ContinuationImpl.kt:30:39)
    at 11  MultiPlatformLibrary                0x0000000105828c66 kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal + 950 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:243:18)
    at 12  MultiPlatformLibrary                0x00000001058287e5 kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal + 1717 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:199:21)
    at 13  MultiPlatformLibrary                0x000000010582a9b9 kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal + 377 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:149:13)
    at 14  MultiPlatformLibrary                0x0000000105517f3a kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 1450 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/coroutines/ContinuationImpl.kt:43:32)
    at 15  MultiPlatformLibrary                0x0000000105828c66 kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal + 950 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:243:18)
    at 16  MultiPlatformLibrary                0x00000001058287e5 kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal + 1717 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:199:21)
    at 17  MultiPlatformLibrary                0x000000010582a9b9 kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal + 377 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:149:13)
    at 18  MultiPlatformLibrary                0x0000000105517f3a kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 1450 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/coroutines/ContinuationImpl.kt:43:32)
    at 19  MultiPlatformLibrary                0x0000000105828c66 kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal + 950 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:243:18)
    at 20  MultiPlatformLibrary                0x00000001058287e5 kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal + 1717 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:199:21)
    at 21  MultiPlatformLibrary                0x000000010582a9b9 kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal + 377 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:149:13)
    at 22  MultiPlatformLibrary                0x0000000105517f3a kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 1450 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/coroutines/ContinuationImpl.kt:43:32)
    at 23  MultiPlatformLibrary                0x0000000105828c66 kfun:io.ktor.util.pipeline.SuspendFunctionGun.resumeRootWith#internal + 950 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:243:18)
    at 24  MultiPlatformLibrary                0x00000001058287e5 kfun:io.ktor.util.pipeline.SuspendFunctionGun.loop#internal + 1717 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:199:21)
    at 25  MultiPlatformLibrary                0x000000010582a9b9 kfun:io.ktor.util.pipeline.SuspendFunctionGun.object-1.resumeWith#internal + 377 (/opt/buildAgent/work/a85294440dc5c6e/ktor-utils/common/src/io/ktor/util/pipeline/PipelineContext.kt:149:13)
    at 26  MultiPlatformLibrary                0x0000000105517f3a kfun:kotlin.coroutines.native.internal.BaseContinuationImpl#resumeWith(kotlin.Result<kotlin.Any?>){} + 1450 (/Users/teamcity/buildAgent/work/cae0e6559deed4c4/runtime/src/main/kotlin/kotlin/coroutines/ContinuationImpl.kt:43:32)
    at 27  MultiPlatformLibrary                0x00000001056dae92 kfun:kotlinx.coroutines.DispatchedTask#run(){} + 2802 (/opt/buildAgent/work/44ec6e850d5c63f0/kotlinx-coroutines-core/common/src/internal/DispatchedTask.kt:39:50)
    at 28  MultiPlatformLibrary                0x00000001059fa508 kfun:dev.icerock.moko.mvvm.viewmodel.UIDispatcher.dispatch$lambda-0#internal + 88 (/Users/runner/work/moko-mvvm/moko-mvvm/mvvm/src/iosMain/kotlin/dev/icerock/moko/mvvm/viewmodel/UIDispatcher.kt:17:19)
    at 29  MultiPlatformLibrary                0x00000001059faa6e kfun:dev.icerock.moko.mvvm.viewmodel.UIDispatcher.$dispatch$lambda-0$FUNCTION_REFERENCE$61.invoke#internal + 62 (/Users/runner/work/moko-mvvm/moko-mvvm/mvvm/src/iosMain/kotlin/dev/icerock/moko/mvvm/viewmodel/UIDispatcher.kt:16:32)

Add public val mEndOfList

Hi. In my case, I need to have access to the property private val mEndOfList. Is it possible to make it a public property?

llegalStateException Try to load next page when list is empty

Fatal Exception: java.lang.IllegalStateException: Try to load next page when list is empty
       at dev.icerock.moko.paging.Pagination.loadNextPage(Pagination.kt:59)
       at com.alliancetrucks.feature.shared.pagination.PaginationViewModel.loadNext(PaginationViewModel.kt:75)
       at com.alliancetrucks.feature.shared.pagination.PaginationScreen$createWidget$$inlined$with$lambda$1$1.invoke(PaginationScreen.kt:39)
       at com.alliancetrucks.feature.shared.pagination.PaginationScreen$createWidget$$inlined$with$lambda$1$1.invoke(PaginationScreen.kt:19)
       at dev.icerock.moko.widgets.core.factory.SystemListViewFactory$TableUnitItemWrapper.bindViewHolder(SystemListViewFactory.kt:118)
       at dev.icerock.moko.units.adapter.UnitsRecyclerViewAdapter.onBindViewHolder(UnitsRecyclerViewAdapter.kt:66)
       at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7065)
       at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7107)
       at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6012)
       at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6279)
       at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
       at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
       at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
       at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
       at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
       at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:665)
       at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4134)
       at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3851)
       at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)
       at android.view.View.layout(View.java:22101)
       at android.view.ViewGroup.layout(ViewGroup.java:6335)
       at androidx.swiperefreshlayout.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:625)
       at android.view.View.layout(View.java:22101)
       at android.view.ViewGroup.layout(ViewGroup.java:6335)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:334)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
       at android.view.View.layout(View.java:22101)
       at android.view.ViewGroup.layout(ViewGroup.java:6335)
       at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
       at android.view.View.layout(View.java:22101)
       at android.view.ViewGroup.layout(ViewGroup.java:6335)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:334)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
       at android.view.View.layout(View.java:22101)
       at android.view.ViewGroup.layout(ViewGroup.java:6335)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1839)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1683)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1592)
       at android.view.View.layout(View.java:22101)
       at android.view.ViewGroup.layout(ViewGroup.java:6335)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:334)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
       at android.view.View.layout(View.java:22101)
       at android.view.ViewGroup.layout(ViewGroup.java:6335)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1839)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1683)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1592)
       at android.view.View.layout(View.java:22101)
       at android.view.ViewGroup.layout(ViewGroup.java:6335)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:334)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
       at android.view.View.layout(View.java:22101)
       at android.view.ViewGroup.layout(ViewGroup.java:6335)
       at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1839)
       at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1683)
       at android.widget.LinearLayout.onLayout(LinearLayout.java:1592)
       at android.view.View.layout(View.java:22101)
       at android.view.ViewGroup.layout(ViewGroup.java:6335)
       at android.widget.FrameLayout.layoutChildren(FrameLayout.java:334)
       at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
       at com.android.internal.policy.DecorView.onLayout(DecorView.java:937)
       at android.view.View.layout(View.java:22101)
       at android.view.ViewGroup.layout(ViewGroup.java:6335)
       at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3280)
       at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2780)
       at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1852)
       at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8084)
       at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1057)
       at android.view.Choreographer.doCallbacks(Choreographer.java:875)
       at android.view.Choreographer.doFrame(Choreographer.java:776)
       at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1042)
       at android.os.Handler.handleCallback(Handler.java:888)
       at android.os.Handler.dispatchMessage(Handler.java:100)
       at android.os.Looper.loop(Looper.java:213)
       at android.app.ActivityThread.main(ActivityThread.java:8147)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:513)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1101)

but logic correct and list should not be empty

Update to Kotlin Coroutines to a newer version

Hi! I'm using your library for pagination logic in commonMain, but unfortunately cannot build the project since Kotlin Coroutines 1.3, which your library uses, are incompatible with 1.5.* which are currently used by e.g. Jetpack Compose. Could you please update the dependencies?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.