Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .idea/copyright/IceRock.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions .idea/copyright/profiles_settings.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ buildscript {
classpath(libs.mobileMultiplatformGradlePlugin)
classpath(libs.kotlinSerializationGradlePlugin)
classpath(libs.mokoUnitsGeneratorGradlePlugin)
classpath(libs.mokoKSwiftGradle)
}
}

Expand Down
12 changes: 9 additions & 3 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ recyclerViewVersion = "1.1.0"
swipeRefreshLayoutVersion = "1.1.0"
ktorClientVersion = "2.0.0"
coroutinesVersion = "1.6.0-native-mt"
mokoMvvmVersion = "0.12.0"
mokoResourcesVersion = "0.18.0"
mokoMvvmVersion = "0.13.0"
mokoResourcesVersion = "0.20.1"
mokoUnitsVersion = "0.8.0"
mokoPagingVersion = "0.7.1"
mokoKSwiftVersion = "0.5.0"
mokoPagingVersion = "0.8.0"

[libraries]
appCompat = { module = "androidx.appcompat:appcompat", version.ref = "androidAppCompatVersion" }
Expand All @@ -24,6 +25,9 @@ mokoUnits = { module = "dev.icerock.moko:units", version.ref = "mokoUnitsVersion
mokoUnitsDataBinding = { module = "dev.icerock.moko:units-databinding", version.ref = "mokoUnitsVersion" }
mokoMvvmLiveData = { module = "dev.icerock.moko:mvvm-livedata", version.ref = "mokoMvvmVersion" }
mokoMvvmState = { module = "dev.icerock.moko:mvvm-state", version.ref = "mokoMvvmVersion" }
mokoMvvmFlow = { module = "dev.icerock.moko:mvvm-flow", version.ref = "mokoMvvmVersion" }
mokoMvvmCore = { module = "dev.icerock.moko:mvvm-core", version.ref = "mokoMvvmVersion" }
mokoMvvmFlowCompose = { module = "dev.icerock.moko:mvvm-flow-compose", version.ref = "mokoMvvmVersion" }
kotlinTestJUnit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlinVersion" }
androidCoreTesting = { module = "androidx.arch.core:core-testing", version.ref = "androidCoreTestingVersion" }
ktorClientMock = { module = "io.ktor:ktor-client-mock", version.ref = "ktorClientVersion" }
Expand All @@ -36,3 +40,5 @@ mokoGradlePlugin = { module = "dev.icerock.moko:moko-gradle-plugin", version = "
mobileMultiplatformGradlePlugin = { module = "dev.icerock:mobile-multiplatform", version = "0.14.1" }
kotlinSerializationGradlePlugin = { module = "org.jetbrains.kotlin:kotlin-serialization", version.ref = "kotlinVersion" }
mokoUnitsGeneratorGradlePlugin = { module = "dev.icerock.moko:units-generator", version.ref = "mokoUnitsVersion" }

mokoKSwiftGradle = { module = "dev.icerock.moko:kswift-gradle-plugin", version.ref = "mokoKSwiftVersion" }
14 changes: 14 additions & 0 deletions paging-core/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* Copyright 2022 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
id("dev.icerock.moko.gradle.multiplatform.mobile")
id("dev.icerock.moko.gradle.publication")
id("dev.icerock.moko.gradle.stub.javadoc")
id("dev.icerock.moko.gradle.detekt")
}

dependencies {
commonMainApi(libs.coroutines)
}
2 changes: 2 additions & 0 deletions paging-core/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="dev.icerock.moko.paging.core" />
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright 2022 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.paging

@Deprecated(
message = "deprecated due to package renaming",
replaceWith = ReplaceWith("IdEntity", "dev.icerock.moko.paging.core"),
level = DeprecationLevel.WARNING
)
typealias IdEntity = dev.icerock.moko.paging.core.IdEntity

@Deprecated(
message = "deprecated due to package renaming",
replaceWith = ReplaceWith("IdComparator", "dev.icerock.moko.paging.core"),
level = DeprecationLevel.WARNING
)
typealias IdComparator<T> = dev.icerock.moko.paging.core.IdComparator<T>

@Deprecated(
message = "deprecated due to package renaming",
replaceWith = ReplaceWith("LambdaPagedListDataSource", "dev.icerock.moko.paging.core"),
level = DeprecationLevel.WARNING
)
typealias LambdaPagedListDataSource<T> = dev.icerock.moko.paging.core.LambdaPagedListDataSource<T>

@Deprecated(
message = "deprecated due to package renaming",
replaceWith = ReplaceWith("PagedListDataSource", "dev.icerock.moko.paging.core"),
level = DeprecationLevel.WARNING
)
typealias PagedListDataSource<T> = dev.icerock.moko.paging.core.PagedListDataSource<T>

@Deprecated(
message = "deprecated due to package renaming",
replaceWith = ReplaceWith("ReachEndNotifierList", "dev.icerock.moko.paging.core"),
level = DeprecationLevel.WARNING
)
typealias ReachEndNotifierList<T> = dev.icerock.moko.paging.core.ReachEndNotifierList<T>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/*
* Copyright 2022 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.paging.core

interface IdEntity {
val id: Long
}

class IdComparator<T : IdEntity> : Comparator<T> {
override fun compare(a: T, b: T): Int {
return if (a.id == b.id) 0 else 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.paging
package dev.icerock.moko.paging.core

class LambdaPagedListDataSource<T>(
private val loadPageLambda: suspend (List<T>?) -> List<T>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.paging
package dev.icerock.moko.paging.core

interface PagedListDataSource<T> {
suspend fun loadPage(currentList: List<T>?): List<T>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,79 +1,77 @@
/*
* Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
* Copyright 2022 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.paging
package dev.icerock.moko.paging.core

import dev.icerock.moko.mvvm.ResourceState
import dev.icerock.moko.mvvm.asState
import dev.icerock.moko.mvvm.livedata.MutableLiveData
import dev.icerock.moko.mvvm.livedata.readOnly
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlin.coroutines.CoroutineContext

class Pagination<Item>(
@Suppress("TooManyFunctions")
abstract class Pagination<Item>(
parentScope: CoroutineScope,
private val dataSource: PagedListDataSource<Item>,
private val comparator: Comparator<Item>,
private val nextPageListener: (Result<List<Item>>) -> Unit,
private val refreshListener: (Result<List<Item>>) -> Unit,
initValue: List<Item>? = null
) : CoroutineScope {

override val coroutineContext: CoroutineContext = parentScope.coroutineContext

private val mStateStorage =
MutableLiveData<ResourceState<List<Item>, Throwable>>(initValue.asStateNullIsLoading())
protected val mNextPageLoading: MutableStateFlow<Boolean> = MutableStateFlow(false)
protected val mRefreshLoading: MutableStateFlow<Boolean> = MutableStateFlow(false)
private val mEndOfList: MutableStateFlow<Boolean> = MutableStateFlow(false)

val state = mStateStorage.readOnly()

private val mNextPageLoading = MutableLiveData(false)
val nextPageLoading = mNextPageLoading.readOnly()

private val mEndOfList = MutableLiveData(false)
private var loadNextPageDeferred: Deferred<List<Item>>? = null
private val listMutex = Mutex()

private val mRefreshLoading = MutableLiveData(false)
val refreshLoading = mRefreshLoading.readOnly()
fun loadFirstPage() {
launch { loadFirstPageSuspend() }
}

private val listMutex = Mutex()
fun loadNextPage() {
launch { loadNextPageSuspend() }
}

private var loadNextPageDeferred: Deferred<List<Item>>? = null
fun refresh() {
launch { refreshSuspend() }
}

fun loadFirstPage() {
launch {
loadFirstPageSuspend()
}
fun setData(items: List<Item>?) {
launch { setDataSuspend(items) }
}

abstract fun dataValue(): List<Item>?
protected abstract fun saveState(items: List<Item>)
protected abstract fun saveStateNullIsEmpty(items: List<Item>?)
protected abstract fun saveStateNullIsLoading(items: List<Item>?)
protected abstract fun saveErrorState(error: Exception)
protected abstract fun setupLoadingState()

suspend fun loadFirstPageSuspend() {
loadNextPageDeferred?.cancel()

listMutex.lock()

mEndOfList.value = false
mNextPageLoading.value = false
mStateStorage.value = ResourceState.Loading()
setupLoadingState()

@Suppress("TooGenericExceptionCaught")
try {
val items: List<Item> = dataSource.loadPage(null)
mStateStorage.value = items.asState()
saveState(items)
} catch (error: Exception) {
mStateStorage.value = ResourceState.Failed(error)
saveErrorState(error)
}
listMutex.unlock()
}

fun loadNextPage() {
launch {
loadNextPageSuspend()
}
}

@Suppress("ReturnCount")
suspend fun loadNextPageSuspend() {
if (mNextPageLoading.value) return
Expand All @@ -87,7 +85,7 @@ class Pagination<Item>(
@Suppress("TooGenericExceptionCaught")
try {
loadNextPageDeferred = this.async {
val currentList = mStateStorage.value.dataValue()
val currentList = dataValue()
?: throw IllegalStateException("Try to load next page when list is empty")
// load next page items
val items = dataSource.loadPage(currentList)
Expand All @@ -103,7 +101,7 @@ class Pagination<Item>(
mEndOfList.value = true
} else {
// save
mStateStorage.value = newList.asState()
saveState(newList)
}
newList
}
Expand All @@ -122,12 +120,6 @@ class Pagination<Item>(
listMutex.unlock()
}

fun refresh() {
launch {
refreshSuspend()
}
}

suspend fun refreshSuspend() {
loadNextPageDeferred?.cancel()
listMutex.lock()
Expand All @@ -148,7 +140,7 @@ class Pagination<Item>(
// load first page items
val items = dataSource.loadPage(null)
// save
mStateStorage.value = items.asState()
saveState(items)
// flag
mEndOfList.value = false
mRefreshLoading.value = false
Expand All @@ -163,34 +155,10 @@ class Pagination<Item>(
listMutex.unlock()
}

fun setData(items: List<Item>?) {
launch {
setDataSuspend(items)
}
}

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

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

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

interface IdEntity {
val id: Long
}

class IdComparator<T : IdEntity> : Comparator<T> {
override fun compare(a: T, b: T): Int {
return if (a.id == b.id) 0 else 1
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* Copyright 2020 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

package dev.icerock.moko.paging
package dev.icerock.moko.paging.core

@Suppress("TooManyFunctions")
class ReachEndNotifierList<T>(
Expand Down
19 changes: 19 additions & 0 deletions paging-flow/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
* Copyright 2022 IceRock MAG Inc. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
id("dev.icerock.moko.gradle.multiplatform.mobile")
id("dev.icerock.moko.gradle.publication")
id("dev.icerock.moko.gradle.stub.javadoc")
id("dev.icerock.moko.gradle.detekt")
}

dependencies {
commonMainApi(libs.mokoMvvmFlow)
commonMainApi(libs.mokoMvvmCore)
commonMainApi(projects.pagingCore)
commonMainApi(projects.pagingState)

commonTestImplementation(projects.pagingTest)
}
2 changes: 2 additions & 0 deletions paging-flow/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="dev.icerock.moko.paging.flow" />
Loading