mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-06 16:49:20 +00:00
fix of InfinityPagedComponent
This commit is contained in:
@@ -2,7 +2,13 @@ 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.coroutines.runCatchingLogging
|
||||||
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 infinite pagination in a Compose UI.
|
* Context for managing infinite pagination in a Compose UI.
|
||||||
@@ -16,30 +22,46 @@ import dev.inmo.micro_utils.pagination.*
|
|||||||
*/
|
*/
|
||||||
class InfinityPagedComponentContext<T> internal constructor(
|
class InfinityPagedComponentContext<T> internal constructor(
|
||||||
page: Int,
|
page: Int,
|
||||||
size: Int
|
size: Int,
|
||||||
|
private val scope: CoroutineScope,
|
||||||
|
private val loader: suspend InfinityPagedComponentContext<T>.(Pagination) -> PaginationResult<T>
|
||||||
) {
|
) {
|
||||||
internal val startPage = SimplePagination(page, size)
|
internal val startPage = SimplePagination(page, size)
|
||||||
internal val currentlyLoadingPage = SpecialMutableStateFlow<Pagination?>(startPage)
|
|
||||||
internal val latestLoadedPage = SpecialMutableStateFlow<PaginationResult<T>?>(null)
|
internal val latestLoadedPage = SpecialMutableStateFlow<PaginationResult<T>?>(null)
|
||||||
internal val dataState = SpecialMutableStateFlow<List<T>?>(null)
|
internal val dataState = SpecialMutableStateFlow<List<T>?>(null)
|
||||||
|
internal var loadingJob: Job? = null
|
||||||
|
internal val loadingMutex = Mutex()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads the next page of data. If the current page is the last one, the function returns early.
|
* Loads the next page of data. If the current page is the last one, the function returns early.
|
||||||
*/
|
*/
|
||||||
fun loadNext() {
|
fun loadNext(): Job {
|
||||||
if (latestLoadedPage.value ?.isLastPage == true) return
|
return scope.launchLoggingDropExceptions {
|
||||||
if (currentlyLoadingPage.value != null) return // Data loading has been inited but not loaded yet
|
loadingMutex.withLock {
|
||||||
|
if (latestLoadedPage.value ?.isLastPage == true) return@launchLoggingDropExceptions
|
||||||
currentlyLoadingPage.value = latestLoadedPage.value ?.nextPage() ?: startPage
|
loadingJob = loadingJob ?: scope.launchLoggingDropExceptions {
|
||||||
|
runCatching {
|
||||||
|
loader(latestLoadedPage.value ?.nextPage() ?: startPage)
|
||||||
|
}.onSuccess {
|
||||||
|
latestLoadedPage.value = it
|
||||||
|
dataState.value = (dataState.value ?: emptyList()) + it.results
|
||||||
|
}
|
||||||
|
loadingMutex.withLock {
|
||||||
|
loadingJob = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadingJob
|
||||||
|
} ?.join()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reloads the pagination from the first page, clearing previously loaded data.
|
* Reloads the pagination from the first page, clearing previously loaded data.
|
||||||
*/
|
*/
|
||||||
fun reload() {
|
fun reload(): Job {
|
||||||
latestLoadedPage.value = null
|
latestLoadedPage.value = null
|
||||||
currentlyLoadingPage.value = null
|
dataState.value = null
|
||||||
loadNext()
|
return loadNext()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,18 +80,11 @@ internal fun <T> InfinityPagedComponent(
|
|||||||
page: Int,
|
page: Int,
|
||||||
size: Int,
|
size: Int,
|
||||||
loader: suspend InfinityPagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
loader: suspend InfinityPagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
predefinedScope: CoroutineScope? = null,
|
||||||
block: @Composable InfinityPagedComponentContext<T>.(List<T>?) -> Unit
|
block: @Composable InfinityPagedComponentContext<T>.(List<T>?) -> Unit
|
||||||
) {
|
) {
|
||||||
val context = remember { InfinityPagedComponentContext<T>(page, size) }
|
val scope = predefinedScope ?: rememberCoroutineScope()
|
||||||
|
val context = remember { InfinityPagedComponentContext<T>(page, size, scope, loader) }
|
||||||
val currentlyLoadingState = context.currentlyLoadingPage.collectAsState()
|
|
||||||
LaunchedEffect(currentlyLoadingState.value) {
|
|
||||||
val paginationResult = loader(context, currentlyLoadingState.value ?: return@LaunchedEffect)
|
|
||||||
context.latestLoadedPage.value = paginationResult
|
|
||||||
context.currentlyLoadingPage.value = null
|
|
||||||
|
|
||||||
context.dataState.value = (context.dataState.value ?: emptyList()) + paginationResult.results
|
|
||||||
}
|
|
||||||
|
|
||||||
val dataState = context.dataState.collectAsState()
|
val dataState = context.dataState.collectAsState()
|
||||||
context.block(dataState.value)
|
context.block(dataState.value)
|
||||||
@@ -88,12 +103,14 @@ internal fun <T> InfinityPagedComponent(
|
|||||||
fun <T> InfinityPagedComponent(
|
fun <T> InfinityPagedComponent(
|
||||||
pageInfo: Pagination,
|
pageInfo: Pagination,
|
||||||
loader: suspend InfinityPagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
loader: suspend InfinityPagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
predefinedScope: CoroutineScope? = null,
|
||||||
block: @Composable InfinityPagedComponentContext<T>.(List<T>?) -> Unit
|
block: @Composable InfinityPagedComponentContext<T>.(List<T>?) -> Unit
|
||||||
) {
|
) {
|
||||||
InfinityPagedComponent(
|
InfinityPagedComponent(
|
||||||
pageInfo.page,
|
pageInfo.page,
|
||||||
pageInfo.size,
|
pageInfo.size,
|
||||||
loader,
|
loader,
|
||||||
|
predefinedScope,
|
||||||
block
|
block
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -111,7 +128,8 @@ fun <T> InfinityPagedComponent(
|
|||||||
fun <T> InfinityPagedComponent(
|
fun <T> InfinityPagedComponent(
|
||||||
size: Int,
|
size: Int,
|
||||||
loader: suspend InfinityPagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
loader: suspend InfinityPagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
predefinedScope: CoroutineScope? = null,
|
||||||
block: @Composable InfinityPagedComponentContext<T>.(List<T>?) -> Unit
|
block: @Composable InfinityPagedComponentContext<T>.(List<T>?) -> Unit
|
||||||
) {
|
) {
|
||||||
InfinityPagedComponent(0, size, loader, block)
|
InfinityPagedComponent(0, size, loader, predefinedScope, block)
|
||||||
}
|
}
|
||||||
|
@@ -30,13 +30,13 @@ class InfinityPagedComponentTests {
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
if (it == null) {
|
if (it == null) {
|
||||||
assertEquals(0, this.currentlyLoadingPage.value ?.page)
|
assertEquals(null, it)
|
||||||
} else {
|
} else {
|
||||||
assertEquals(expectedList, it)
|
assertEquals(expectedList, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(it ?.size) {
|
LaunchedEffect(it ?.size) {
|
||||||
loadNext()
|
loadNext().join()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user