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

[WIP] Paging Refactor #606

Closed
wants to merge 11 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

package org.mobilenativefoundation.store.cache5

import org.mobilenativefoundation.store.core5.ExperimentalStoreApi
import org.mobilenativefoundation.store.core5.KeyProvider
import org.mobilenativefoundation.store.core5.StoreData
import org.mobilenativefoundation.store.core5.StoreKey
Expand All @@ -12,7 +13,8 @@ import org.mobilenativefoundation.store.core5.StoreKey
* Depends on [StoreMultiCacheAccessor] for internal data management.
* @see [Cache].
*/
class StoreMultiCache<Id : Any, Key : StoreKey<Id>, Single : StoreData.Single<Id>, Collection : StoreData.Collection<Id, Single>, Output : StoreData<Id>>(
@OptIn(ExperimentalStoreApi::class)
class StoreMultiCache<Id : Any, Key : StoreKey<Id>, Single : StoreData.Single<Id>, Collection : StoreData.Collection<Id, *, Single>, Output : StoreData<Id>>(
private val keyProvider: KeyProvider<Id, Single>,
singlesCache: Cache<StoreKey.Single<Id>, Single> = CacheBuilder<StoreKey.Single<Id>, Single>().build(),
collectionsCache: Cache<StoreKey.Collection<Id>, Collection> = CacheBuilder<StoreKey.Collection<Id>, Collection>().build(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.mobilenativefoundation.store.cache5

import kotlinx.atomicfu.locks.SynchronizedObject
import kotlinx.atomicfu.locks.synchronized
import org.mobilenativefoundation.store.core5.ExperimentalStoreApi
import org.mobilenativefoundation.store.core5.StoreData
import org.mobilenativefoundation.store.core5.StoreKey

Expand All @@ -20,7 +21,8 @@ import org.mobilenativefoundation.store.core5.StoreKey
* @property singlesCache The cache used to store single data items.
* @property collectionsCache The cache used to store collections of data items.
*/
class StoreMultiCacheAccessor<Id : Any, Collection : StoreData.Collection<Id, Single>, Single : StoreData.Single<Id>>(
@ExperimentalStoreApi
class StoreMultiCacheAccessor<Id : Any, Collection : StoreData.Collection<Id, *, Single>, Single : StoreData.Single<Id>>(
private val singlesCache: Cache<StoreKey.Single<Id>, Single>,
private val collectionsCache: Cache<StoreKey.Collection<Id>, Collection>,
) : SynchronizedObject() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ interface StoreData<out Id : Any> {
/**
* Represents a collection of identifiable items.
*/
interface Collection<Id : Any, S : Single<Id>> : StoreData<Id> {
val items: List<S>
interface Collection<Id : Any, CK: StoreKey.Collection<Id>, SO : Single<Id>> : StoreData<Id> {
val items: List<SO>
val itemsBefore: Int?
val itemsAfter: Int?
val prevKey: CK
val nextKey: CK?

/**
* Returns a new collection with the updated items.
*/
fun copyWith(items: List<S>): Collection<Id, S>
fun copyWith(items: List<SO>): Collection<Id, *, SO>

/**
* Inserts items to the existing collection and returns the updated collection.
*/
fun insertItems(strategy: InsertionStrategy, items: List<S>): Collection<Id, S>
fun insertItems(strategy: InsertionStrategy, items: List<SO>): Collection<Id, *, SO>
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ interface StoreKey<out Id : Any> {
/**
* Represents a key for fetching an individual item.
*/
interface Single<Id : Any> : StoreKey<Id> {
interface Single<out Id : Any> : StoreKey<Id> {
val id: Id
}

Expand Down
18 changes: 12 additions & 6 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,28 @@ androidMinSdk = "24"
androidCompileSdk = "33"
androidGradlePlugin = "7.4.2"
androidTargetSdk = "33"
atomicFu = "0.20.2"
baseKotlin = "1.9.10"
atomicFu = "0.23.1"
baseKotlin = "1.9.22"
dokkaGradlePlugin = "1.6.0"
ktlintGradle = "10.2.1"
ktlintGradle = "11.0.0"
jacocoGradlePlugin = "0.8.7"
mavenPublishPlugin = "0.22.0"
moleculeGradlePlugin = "1.2.1"
pagingCompose = "3.3.0-alpha02"
pagingCompose = "3.3.0-alpha03"
pagingRuntime = "3.2.1"
spotlessPluginGradle = "6.4.1"
junit = "4.13.2"
kotlinxCoroutines = "1.7.1"
kotlinxSerialization = "1.5.1"
kotlinxCoroutines = "1.7.3"
kotlinxSerialization = "1.6.3"
kermit = "1.2.2"
testCore = "1.5.0"
kmmBridge = "0.3.2"
ktlint = "0.39.0"
kover = "0.6.0"
store = "5.1.0-alpha02"
truth = "1.1.3"
jetbrains-compose = "1.5.12"


[libraries]
android-gradle-plugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" }
Expand Down Expand Up @@ -54,3 +56,7 @@ junit = { group = "junit", name = "junit", version.ref = "junit" }
google-truth = { group = "com.google.truth", name = "truth", version.ref = "truth" }
touchlab-kermit = { group = "co.touchlab", name = "kermit", version.ref = "kermit" }
turbine = "app.cash.turbine:turbine:1.0.0"


[plugins]
compose = { id = "org.jetbrains.compose", version.ref = "jetbrains-compose" }
124 changes: 124 additions & 0 deletions paging/compose/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
import com.vanniktech.maven.publish.SonatypeHost
import org.jetbrains.dokka.gradle.DokkaTask

plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
id("com.android.library")
id("com.vanniktech.maven.publish")
id("org.jetbrains.dokka")
id("org.jetbrains.kotlinx.kover")
id("co.touchlab.faktory.kmmbridge") version("0.3.2")
`maven-publish`
kotlin("native.cocoapods")
id("kotlinx-atomicfu")
alias(libs.plugins.compose)
}

kotlin {
android()
jvm()
iosArm64()
iosX64()
linuxX64()
iosSimulatorArm64()
js {
browser()
nodejs()
}
cocoapods {
summary = "Store5/Paging"
homepage = "https://github.com/MobileNativeFoundation/Store"
ios.deploymentTarget = "13"
version = libs.versions.store.get()
}

sourceSets {
val commonMain by getting {
dependencies {
implementation(libs.kotlin.stdlib)
implementation(project(":store"))
implementation(project(":cache"))
api(project(":core"))
implementation(compose.runtime)
implementation(libs.kotlinx.coroutines.core)
api(project(":paging"))
}
}

val androidMain by getting
val commonTest by getting {
dependencies {
implementation(kotlin("test"))
implementation(libs.turbine)
implementation(libs.kotlinx.coroutines.test)
}
}
}
}

android {
sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml")
compileSdk = 33

defaultConfig {
minSdk = 24
targetSdk = 33
}

lint {
disable += "ComposableModifierFactory"
disable += "ModifierFactoryExtensionFunction"
disable += "ModifierFactoryReturnType"
disable += "ModifierFactoryUnreferencedReceiver"
}

compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
}

tasks.withType<DokkaTask>().configureEach {
dokkaSourceSets.configureEach {
reportUndocumented.set(false)
skipDeprecated.set(true)
jdkVersion.set(11)
}
}

mavenPublishing {
publishToMavenCentral(SonatypeHost.S01)
signAllPublications()
}

addGithubPackagesRepository()
kmmbridge {
githubReleaseArtifacts()
githubReleaseVersions()
versionPrefix.set(libs.versions.store.get())
spm()
}

koverMerged {
enable()

xmlReport {
onCheck.set(true)
reportFile.set(layout.projectDirectory.file("kover/coverage.xml"))
}

htmlReport {
onCheck.set(true)
reportDir.set(layout.projectDirectory.dir("kover/html"))
}

verify {
onCheck.set(true)
}
}

atomicfu {
transformJvm = false
transformJs = false
}
3 changes: 3 additions & 0 deletions paging/compose/gradle.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POM_NAME=org.mobilenativefoundation.store
POM_ARTIFACT_ID=paging-compose
POM_PACKAGING=jar
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package org.mobilenativefoundation.store.paging.compose

import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
import org.mobilenativefoundation.store.core5.ExperimentalStoreApi
import org.mobilenativefoundation.store.core5.StoreData
import org.mobilenativefoundation.store.core5.StoreKey
import org.mobilenativefoundation.store.paging5.PageAggregatingStrategy
import org.mobilenativefoundation.store.paging5.PageFetchingStrategy
import org.mobilenativefoundation.store.paging5.Pager
import org.mobilenativefoundation.store.paging5.PagingConfig
import org.mobilenativefoundation.store.paging5.PagingSource
import org.mobilenativefoundation.store.paging5.impl.DefaultPageAggregatingStrategy
import org.mobilenativefoundation.store.paging5.impl.DefaultPageFetchingStrategy

@Composable
@ExperimentalStoreApi
inline fun <Id : Comparable<Id>, CK : StoreKey.Collection<Id>, SO : StoreData.Single<Id>> rememberPager(
coroutineScope: CoroutineScope = rememberCoroutineScope(),
initialKey: CK,
anchorPosition: StateFlow<Id?> = MutableStateFlow(null),
pagingConfig: PagingConfig = PagingConfig(),
aggregator: PageAggregatingStrategy<Id, CK, SO> = DefaultPageAggregatingStrategy(),
pageFetchingStrategy: PageFetchingStrategy<Id, CK, SO> = DefaultPageFetchingStrategy(),
noinline pagingSourceFactory: () -> PagingSource<Id, CK, SO>
): Pager<Id, CK, SO> = remember(coroutineScope, initialKey, anchorPosition, pagingConfig, aggregator, pageFetchingStrategy, pagingSourceFactory) {
Pager.create(
scope = coroutineScope,
initialKey = initialKey,
anchorPosition = anchorPosition,
pagingConfig = pagingConfig,
aggregator = aggregator,
pageFetchingStrategy = pageFetchingStrategy,
pagingSourceFactory = pagingSourceFactory
)
}



@Composable
@ExperimentalStoreApi
fun <Id : Comparable<Id>, CK : StoreKey.Collection<Id>, SO : StoreData.Single<Id>> Pager(
pager: Pager<Id, CK, SO>,
content: @Composable (data: Pager.PagingData<Id, SO>, error: Pager.PagingError?) -> Unit
) {

val combinedFlow = combine(pager.data, pager.errors) { data, error ->
data to error
}

val (data, error) = combinedFlow.collectAsState(Pager.PagingData(emptyList<SO>()) to null).value

content(data, error)
}
Loading
Loading