update paged component

This commit is contained in:
2025-03-08 07:59:27 +06:00
parent 6df8ad3095
commit 618f2dcd79
2 changed files with 64 additions and 24 deletions

View File

@@ -86,7 +86,7 @@ internal fun <T> InfinityPagedComponent(
val scope = predefinedScope ?: rememberCoroutineScope() val scope = predefinedScope ?: rememberCoroutineScope()
val context = remember { InfinityPagedComponentContext<T>(page, size, scope, loader) } val context = remember { InfinityPagedComponentContext<T>(page, size, scope, loader) }
remember { remember {
context.loadNext() context.reload()
} }
val dataState = context.dataState.collectAsState() val dataState = context.dataState.collectAsState()

View File

@@ -2,7 +2,12 @@ package dev.inmo.micro_utils.pagination.compose
import androidx.compose.runtime.* import androidx.compose.runtime.*
import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow import dev.inmo.micro_utils.coroutines.SpecialMutableStateFlow
import dev.inmo.micro_utils.coroutines.launchLoggingDropExceptions
import dev.inmo.micro_utils.pagination.* import dev.inmo.micro_utils.pagination.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
/** /**
* Context for managing paginated data in a Compose UI. * Context for managing paginated data in a Compose UI.
@@ -18,39 +23,73 @@ import dev.inmo.micro_utils.pagination.*
*/ */
class PagedComponentContext<T> internal constructor( class PagedComponentContext<T> internal constructor(
initialPage: Int, initialPage: Int,
size: Int size: Int,
private val scope: CoroutineScope,
private val loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>
) { ) {
internal val startPage = SimplePagination(initialPage, size) internal val startPage = SimplePagination(initialPage, size)
internal val currentlyLoadingPageState = SpecialMutableStateFlow<Pagination?>(startPage)
internal val latestLoadedPage = SpecialMutableStateFlow<PaginationResult<T>?>(null) internal val latestLoadedPage = SpecialMutableStateFlow<PaginationResult<T>?>(null)
internal val dataState = SpecialMutableStateFlow<PaginationResult<T>?>(null)
internal var loadingJob: Job? = null
internal val loadingMutex = Mutex()
private fun initLoadingJob(
skipCheckerInLock: () -> Boolean,
pageGetter: () -> Pagination
): Job {
return scope.launchLoggingDropExceptions {
loadingMutex.withLock {
if (skipCheckerInLock()) return@launchLoggingDropExceptions
loadingJob = loadingJob ?: scope.launchLoggingDropExceptions {
runCatching {
loader(pageGetter())
}.onSuccess {
latestLoadedPage.value = it
dataState.value = it
}
loadingMutex.withLock {
loadingJob = null
}
}
loadingJob
} ?.join()
}
}
/** /**
* Loads the next page of data. If the last page is reached, this function returns early. * Loads the next page of data. If the last page is reached, this function returns early.
*/ */
fun loadNext() { fun loadNext(): Job {
when { return initLoadingJob(
currentlyLoadingPageState.value != null -> return { latestLoadedPage.value ?.isLastPage == true }
latestLoadedPage.value ?.isLastPage == true -> return ) {
else -> currentlyLoadingPageState.value = (latestLoadedPage.value ?.nextPage()) ?: startPage latestLoadedPage.value ?.nextPage() ?: startPage
} }
} }
/** /**
* Loads the previous page of data if available. * Loads the previous page of data if available.
*/ */
fun loadPrevious() { fun loadPrevious(): Job {
when { return initLoadingJob(
currentlyLoadingPageState.value != null -> return { latestLoadedPage.value ?.isFirstPage == true }
latestLoadedPage.value ?.isFirstPage == true -> return ) {
else -> currentlyLoadingPageState.value = (latestLoadedPage.value ?.previousPage()) ?: startPage latestLoadedPage.value ?.previousPage() ?: startPage
} }
} }
/** /**
* Reloads the current page, refreshing the data. * Reloads the current page, refreshing the data.
*/ */
fun reload() { fun reload(): Job {
currentlyLoadingPageState.value = latestLoadedPage.value return initLoadingJob(
{
latestLoadedPage.value = null
true
}
) {
startPage
}
} }
} }
@@ -69,18 +108,16 @@ internal fun <T> PagedComponent(
initialPage: Int, initialPage: Int,
size: Int, size: Int,
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>, loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
predefinedScope: CoroutineScope? = null,
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
) { ) {
val context = remember { PagedComponentContext<T>(initialPage, size) } val scope = predefinedScope ?: rememberCoroutineScope()
val context = remember { PagedComponentContext<T>(initialPage, size, scope, loader) }
val currentlyLoadingState = context.currentlyLoadingPageState.collectAsState() remember {
LaunchedEffect(currentlyLoadingState.value) { context.reload()
val paginationResult = loader(context, currentlyLoadingState.value ?: return@LaunchedEffect)
context.latestLoadedPage.value = paginationResult
context.currentlyLoadingPageState.value = null
} }
val pageState = context.latestLoadedPage.collectAsState() val pageState = context.dataState.collectAsState()
pageState.value ?.let { pageState.value ?.let {
context.block(it) context.block(it)
} }
@@ -98,12 +135,14 @@ internal fun <T> PagedComponent(
fun <T> PagedComponent( fun <T> PagedComponent(
pageInfo: Pagination, pageInfo: Pagination,
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>, loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
predefinedScope: CoroutineScope? = null,
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
) { ) {
PagedComponent( PagedComponent(
pageInfo.page, pageInfo.page,
pageInfo.size, pageInfo.size,
loader, loader,
predefinedScope,
block block
) )
} }
@@ -120,7 +159,8 @@ fun <T> PagedComponent(
fun <T> PagedComponent( fun <T> PagedComponent(
size: Int, size: Int,
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>, loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
predefinedScope: CoroutineScope? = null,
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
) { ) {
PagedComponent(0, size, loader, block) PagedComponent(0, size, loader, predefinedScope, block)
} }