All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.io.github.ahmad_hamwi.compose.pagination.PaginationState.kt Maven / Gradle / Ivy

package io.github.ahmad_hamwi.compose.pagination

import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember

@Suppress("UNCHECKED_CAST")
@Stable
class PaginationState(
    initialPageKey: KEY,
    internal val onRequestPage: PaginationState.(KEY) -> Unit
) {
    internal var internalState =
        mutableStateOf>(
            PaginationInternalState.Initial(
                initialPageKey
            )
        )

    val requestedPageKey: KEY?
        get() = (internalState.value as? PaginationInternalState.IHasRequestedPageKey)?.requestedPageKey

    /**
     * All the items currently loaded so far. null when no items has been loaded yet.
     */
    val allItems: List?
        get() = internalState.value.items

    fun setError(exception: Exception) {
        val internalStateSnapshot = internalState.value
        val nextPageKeyOfLoadingState: KEY? =
            (internalStateSnapshot as? PaginationInternalState.Loaded)?.nextPageKey
        val requestedPageKeyOfLoadingOrErrorState: KEY? =
            (internalStateSnapshot as? PaginationInternalState.IHasRequestedPageKey)?.requestedPageKey

        // requestedPageKey is going to either be
        // 1- the nextPageKey of the loaded state: because the page has already been loaded and showing an error means a new page error state
        // OR
        // 2- the requestedPageKey of either a loading or a previous error state
        internalState.value = PaginationInternalState.Error(
            initialPageKey = internalStateSnapshot.initialPageKey,
            requestedPageKey = nextPageKeyOfLoadingState
                ?: requestedPageKeyOfLoadingOrErrorState
                ?: internalStateSnapshot.initialPageKey,
            exception = exception,
            items = internalState.value.items
        )
    }

    @OptIn(ExperimentalPaginationApi::class)
    fun appendPage(items: List, nextPageKey: KEY, isLastPage: Boolean = false) {
        appendPageWithUpdates(
            allItems = (internalState.value.items ?: listOf()) + items,
            nextPageKey = nextPageKey,
            isLastPage = isLastPage
        )
    }


    /**
     * Updates current allItems but should also include new changes from a new page.
     * This allows you to do a full list update while still do the same `appendPage()` behavior
     * in one shot, rather than having to set allItems first then call `appendPage()`.
     *
     * This API is experimental as it may be confusing to some users, naming can change without
     * notice and can be subject to removal.
     */
    @ExperimentalPaginationApi
    fun appendPageWithUpdates(allItems: List, nextPageKey: KEY, isLastPage: Boolean = false) {
        val internalStateSnapshot = internalState.value
        val requestedPageKeyOfLoadingOrErrorState: KEY? =
            (internalStateSnapshot as? PaginationInternalState.IHasRequestedPageKey)?.requestedPageKey

        internalState.value = PaginationInternalState.Loaded(
            initialPageKey = internalState.value.initialPageKey,
            requestedPageKey = requestedPageKeyOfLoadingOrErrorState
                ?: internalStateSnapshot.initialPageKey,
            nextPageKey = nextPageKey,
            items = allItems,
            isLastPage = isLastPage
        )
    }

    fun retryLastFailedRequest() {
        val internalStateSnapshot = internalState.value

        require(internalStateSnapshot is PaginationInternalState.Error) {
            "retryLastFailedRequest is invoked while there were no errors has been set using setError"
        }

        internalState.value = PaginationInternalState.Loading(
            initialPageKey = internalStateSnapshot.initialPageKey,
            requestedPageKey = internalStateSnapshot.requestedPageKey,
            items = internalStateSnapshot.items
        )
    }

    fun refresh(initialPageKey: KEY? = null) {
        internalState.value = PaginationInternalState.Initial(
            initialPageKey ?: internalState.value.initialPageKey
        )
    }
}

@Composable
fun  rememberPaginationState(
    initialPageKey: KEY,
    onRequestPage: PaginationState.(KEY) -> Unit
): PaginationState {
    return remember {
        PaginationState(
            initialPageKey = initialPageKey,
            onRequestPage = onRequestPage
        )
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy