mirror of
https://github.com/InsanusMokrassar/MicroUtils.git
synced 2025-09-06 16:49:20 +00:00
start add paged/loading component
This commit is contained in:
@@ -0,0 +1,74 @@
|
|||||||
|
package dev.inmo.micro_utils.common.compose
|
||||||
|
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import dev.inmo.micro_utils.common.Optional
|
||||||
|
import dev.inmo.micro_utils.common.dataOrThrow
|
||||||
|
import dev.inmo.micro_utils.common.optional
|
||||||
|
|
||||||
|
class LoadableComponentContext<T> internal constructor(
|
||||||
|
presetOptional: Optional<T>,
|
||||||
|
) {
|
||||||
|
internal val iterationState: MutableState<Int> = mutableStateOf(0)
|
||||||
|
|
||||||
|
internal var dataOptional: Optional<T> = if (presetOptional.dataPresented) presetOptional else Optional.absent()
|
||||||
|
private set
|
||||||
|
internal val dataState: MutableState<Optional<T>> = mutableStateOf(dataOptional)
|
||||||
|
|
||||||
|
fun reload() {
|
||||||
|
iterationState.value++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showing data with ability to reload data
|
||||||
|
*
|
||||||
|
* [block] will be shown when [loader] will complete loading. If you want to reload data, just call
|
||||||
|
* [LoadableComponentContext.reload]
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun <T> LoadableComponent(
|
||||||
|
preload: Optional<T>,
|
||||||
|
loader: suspend LoadableComponentContext<T>.() -> T,
|
||||||
|
block: @Composable LoadableComponentContext<T>.(T) -> Unit
|
||||||
|
) {
|
||||||
|
val context = remember { LoadableComponentContext(preload) }
|
||||||
|
|
||||||
|
LaunchedEffect(context.iterationState.value) {
|
||||||
|
context.dataState.value = loader(context).optional
|
||||||
|
}
|
||||||
|
|
||||||
|
context.dataState.let {
|
||||||
|
if (it.value.dataPresented) {
|
||||||
|
context.block(it.value.dataOrThrow(IllegalStateException("Data must be presented, but optional has been changed by some way")))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showing data with ability to reload data
|
||||||
|
*
|
||||||
|
* [block] will be shown when [loader] will complete loading. If you want to reload data, just call
|
||||||
|
* [LoadableComponentContext.reload]
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun <T> LoadableComponent(
|
||||||
|
preload: T,
|
||||||
|
loader: suspend LoadableComponentContext<T>.() -> T,
|
||||||
|
block: @Composable LoadableComponentContext<T>.(T) -> Unit
|
||||||
|
) {
|
||||||
|
LoadableComponent(preload.optional, loader, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Showing data with ability to reload data
|
||||||
|
*
|
||||||
|
* [block] will be shown when [loader] will complete loading. If you want to reload data, just call
|
||||||
|
* [LoadableComponentContext.reload]
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun <T> LoadableComponent(
|
||||||
|
loader: suspend LoadableComponentContext<T>.() -> T,
|
||||||
|
block: @Composable LoadableComponentContext<T>.(T) -> Unit
|
||||||
|
) {
|
||||||
|
LoadableComponent(Optional.absent(), loader, block)
|
||||||
|
}
|
@@ -40,6 +40,8 @@ fun Pagination.intersect(
|
|||||||
inline val Pagination.isFirstPage
|
inline val Pagination.isFirstPage
|
||||||
get() = page == 0
|
get() = page == 0
|
||||||
|
|
||||||
|
fun Pagination.firstPage() = if (isFirstPage) this else SimplePagination(0, size)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* First number in index of objects. It can be used as offset for databases or other data sources
|
* First number in index of objects. It can be used as offset for databases or other data sources
|
||||||
*/
|
*/
|
||||||
|
20
pagination/compose/build.gradle
Normal file
20
pagination/compose/build.gradle
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
plugins {
|
||||||
|
id "org.jetbrains.kotlin.multiplatform"
|
||||||
|
id "org.jetbrains.kotlin.plugin.serialization"
|
||||||
|
id "com.android.library"
|
||||||
|
alias(libs.plugins.jb.compose)
|
||||||
|
alias(libs.plugins.kt.jb.compose)
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "$mppComposeJvmJsAndroidLinuxMingwLinuxArm64Project"
|
||||||
|
|
||||||
|
kotlin {
|
||||||
|
sourceSets {
|
||||||
|
commonMain {
|
||||||
|
dependencies {
|
||||||
|
api project(":micro_utils.pagination.common")
|
||||||
|
api project(":micro_utils.common.compose")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,99 @@
|
|||||||
|
package dev.inmo.micro_utils.pagination.compose
|
||||||
|
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import dev.inmo.micro_utils.common.Optional
|
||||||
|
import dev.inmo.micro_utils.common.dataOrThrow
|
||||||
|
import dev.inmo.micro_utils.common.optional
|
||||||
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
|
||||||
|
class InfinityPagedComponentContext<T> internal constructor(
|
||||||
|
preset: List<T>? = null,
|
||||||
|
initialPage: Int,
|
||||||
|
size: Int
|
||||||
|
) {
|
||||||
|
internal val iterationState: MutableState<Pair<Int, Pagination>> = mutableStateOf(0 to SimplePagination(preset ?.page ?: initialPage, preset ?.size ?: size))
|
||||||
|
|
||||||
|
internal var dataOptional: List<T>? = preset
|
||||||
|
private set
|
||||||
|
internal val dataState: MutableState<List<T>?> = mutableStateOf(dataOptional)
|
||||||
|
|
||||||
|
fun loadNext() {
|
||||||
|
iterationState.value = iterationState.value.let {
|
||||||
|
if ((dataState.value as? PaginationResult<*>) ?.isLastPage == true) return
|
||||||
|
(it.first + 1) to it.second.nextPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun reload() {
|
||||||
|
iterationState.value = iterationState.value.let {
|
||||||
|
(it.first + 1) to (it.second.firstPage())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun <T> InfinityPagedComponent(
|
||||||
|
preload: List<T>?,
|
||||||
|
initialPage: Int,
|
||||||
|
size: Int,
|
||||||
|
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
block: @Composable PagedComponentContext<T>.(List<T>) -> Unit
|
||||||
|
) {
|
||||||
|
val context = remember { InfinityPagedComponentContext(preload, initialPage, size) }
|
||||||
|
|
||||||
|
LaunchedEffect(context.iterationState.value) {
|
||||||
|
context.dataState.value = loader(context, context.iterationState.value.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.dataState.value ?.let {
|
||||||
|
context.block()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> InfinityPagedComponent(
|
||||||
|
preload: PaginationResult<T>,
|
||||||
|
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
||||||
|
) {
|
||||||
|
PagedComponent(
|
||||||
|
preload,
|
||||||
|
preload.page,
|
||||||
|
preload.size,
|
||||||
|
loader,
|
||||||
|
block
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> InfinityPagedComponent(
|
||||||
|
pageInfo: Pagination,
|
||||||
|
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
||||||
|
) {
|
||||||
|
PagedComponent(
|
||||||
|
null,
|
||||||
|
pageInfo.page,
|
||||||
|
pageInfo.size,
|
||||||
|
loader,
|
||||||
|
block
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> InfinityPagedComponent(
|
||||||
|
initialPage: Int,
|
||||||
|
size: Int,
|
||||||
|
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
||||||
|
) {
|
||||||
|
PagedComponent(null, initialPage, size, loader, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> InfinityPagedComponent(
|
||||||
|
size: Int,
|
||||||
|
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
||||||
|
) {
|
||||||
|
PagedComponent(0, size, loader, block)
|
||||||
|
}
|
108
pagination/compose/src/commonMain/kotlin/PagedComponent.kt
Normal file
108
pagination/compose/src/commonMain/kotlin/PagedComponent.kt
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package dev.inmo.micro_utils.pagination.compose
|
||||||
|
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import dev.inmo.micro_utils.common.Optional
|
||||||
|
import dev.inmo.micro_utils.common.dataOrThrow
|
||||||
|
import dev.inmo.micro_utils.common.optional
|
||||||
|
import dev.inmo.micro_utils.pagination.*
|
||||||
|
|
||||||
|
class PagedComponentContext<T> internal constructor(
|
||||||
|
preset: PaginationResult<T>? = null,
|
||||||
|
initialPage: Int,
|
||||||
|
size: Int
|
||||||
|
) {
|
||||||
|
internal val iterationState: MutableState<Pair<Int, Pagination>> = mutableStateOf(0 to SimplePagination(preset ?.page ?: initialPage, preset ?.size ?: size))
|
||||||
|
|
||||||
|
internal var dataOptional: PaginationResult<T>? = preset
|
||||||
|
private set
|
||||||
|
internal val dataState: MutableState<PaginationResult<T>?> = mutableStateOf(dataOptional)
|
||||||
|
|
||||||
|
fun loadNext() {
|
||||||
|
iterationState.value = iterationState.value.let {
|
||||||
|
if (dataState.value ?.isLastPage == true) return
|
||||||
|
(it.first + 1) to it.second.nextPage()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun loadPrevious() {
|
||||||
|
iterationState.value = iterationState.value.let {
|
||||||
|
if (it.second.isFirstPage) return
|
||||||
|
(it.first - 1) to SimplePagination(
|
||||||
|
it.second.page - 1,
|
||||||
|
it.second.size
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun reload() {
|
||||||
|
iterationState.value = iterationState.value.let {
|
||||||
|
it.copy(it.first + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
internal fun <T> PagedComponent(
|
||||||
|
preload: PaginationResult<T>?,
|
||||||
|
initialPage: Int,
|
||||||
|
size: Int,
|
||||||
|
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
||||||
|
) {
|
||||||
|
val context = remember { PagedComponentContext(preload, initialPage, size) }
|
||||||
|
|
||||||
|
LaunchedEffect(context.iterationState.value) {
|
||||||
|
context.dataState.value = loader(context, context.iterationState.value.second)
|
||||||
|
}
|
||||||
|
|
||||||
|
context.dataState.value ?.let {
|
||||||
|
context.block(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> PagedComponent(
|
||||||
|
preload: PaginationResult<T>,
|
||||||
|
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
||||||
|
) {
|
||||||
|
PagedComponent(
|
||||||
|
preload,
|
||||||
|
preload.page,
|
||||||
|
preload.size,
|
||||||
|
loader,
|
||||||
|
block
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> PagedComponent(
|
||||||
|
pageInfo: Pagination,
|
||||||
|
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
||||||
|
) {
|
||||||
|
PagedComponent(
|
||||||
|
null,
|
||||||
|
pageInfo.page,
|
||||||
|
pageInfo.size,
|
||||||
|
loader,
|
||||||
|
block
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> PagedComponent(
|
||||||
|
initialPage: Int,
|
||||||
|
size: Int,
|
||||||
|
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
||||||
|
) {
|
||||||
|
PagedComponent(null, initialPage, size, loader, block)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> PagedComponent(
|
||||||
|
size: Int,
|
||||||
|
loader: suspend PagedComponentContext<T>.(Pagination) -> PaginationResult<T>,
|
||||||
|
block: @Composable PagedComponentContext<T>.(PaginationResult<T>) -> Unit
|
||||||
|
) {
|
||||||
|
PagedComponent(0, size, loader, block)
|
||||||
|
}
|
@@ -11,6 +11,7 @@ String[] includes = [
|
|||||||
":koin:generator:test",
|
":koin:generator:test",
|
||||||
":selector:common",
|
":selector:common",
|
||||||
":pagination:common",
|
":pagination:common",
|
||||||
|
":pagination:compose",
|
||||||
":pagination:exposed",
|
":pagination:exposed",
|
||||||
":pagination:ktor:common",
|
":pagination:ktor:common",
|
||||||
":pagination:ktor:server",
|
":pagination:ktor:server",
|
||||||
|
Reference in New Issue
Block a user