Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#39 add test, flow, core modules + up version #60

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
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