From 6af03123ef67c4732748a36d51504e2fbd29f6c2 Mon Sep 17 00:00:00 2001 From: mramotar_dbx Date: Sat, 17 Feb 2024 14:44:06 -0500 Subject: [PATCH] Clean up --- .../store/core5/StoreKey.kt | 7 - .../store/paging5/LaunchPagingStore.kt | 251 +++++++++--------- .../store/paging5/LaunchPagingStoreTests.kt | 208 ++++----------- .../store/paging5/util/PostDataJoiner.kt | 30 --- .../store/paging5/util/PostJoiner.kt | 20 ++ .../store/paging5/util/PostKey.kt | 7 +- 6 files changed, 206 insertions(+), 317 deletions(-) delete mode 100644 paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostDataJoiner.kt create mode 100644 paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostJoiner.kt diff --git a/core/src/commonMain/kotlin/org/mobilenativefoundation/store/core5/StoreKey.kt b/core/src/commonMain/kotlin/org/mobilenativefoundation/store/core5/StoreKey.kt index 2a545d022..f5560e7f9 100644 --- a/core/src/commonMain/kotlin/org/mobilenativefoundation/store/core5/StoreKey.kt +++ b/core/src/commonMain/kotlin/org/mobilenativefoundation/store/core5/StoreKey.kt @@ -1,12 +1,5 @@ package org.mobilenativefoundation.store.core5 - -@OptIn(ExperimentalStoreApi::class) -interface KeyFactory> { - fun createSingleFor(id: Id): Key -} - - /** * An interface that defines keys used by Store for data-fetching operations. * Allows Store to fetch individual items and collections of items. diff --git a/paging/src/commonMain/kotlin/org/mobilenativefoundation/store/paging5/LaunchPagingStore.kt b/paging/src/commonMain/kotlin/org/mobilenativefoundation/store/paging5/LaunchPagingStore.kt index b3e913521..422c69cf2 100644 --- a/paging/src/commonMain/kotlin/org/mobilenativefoundation/store/paging5/LaunchPagingStore.kt +++ b/paging/src/commonMain/kotlin/org/mobilenativefoundation/store/paging5/LaunchPagingStore.kt @@ -13,7 +13,6 @@ import kotlinx.coroutines.plus import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import org.mobilenativefoundation.store.core5.ExperimentalStoreApi -import org.mobilenativefoundation.store.core5.KeyFactory import org.mobilenativefoundation.store.core5.StoreData import org.mobilenativefoundation.store.core5.StoreKey import org.mobilenativefoundation.store.store5.MutableStore @@ -21,179 +20,185 @@ import org.mobilenativefoundation.store.store5.Store import org.mobilenativefoundation.store.store5.StoreReadRequest import org.mobilenativefoundation.store.store5.StoreReadResponse -@ExperimentalStoreApi -interface StorePager, Output : StoreData> { - val state: StateFlow> - fun load(key: Key) -} @ExperimentalStoreApi -interface DataJoiner, Output : StoreData.Collection, Single : StoreData.Single> { - suspend operator fun invoke( - key: Key, - data: Map?> - ): StoreReadResponse.Data -} +data class PagingData>( + val items: List +) - -/** - * Initializes and returns a [StateFlow] that reflects the state of the [Store], updating by a flow of provided keys. - * @see [launchPagingStore]. - */ @ExperimentalStoreApi -fun , Output : StoreData, SingleKey : StoreKey.Single, Collection : StoreData.Collection, Single : StoreData.Single> MutableStore.launchPagingStore( - scope: CoroutineScope, - keys: Flow, - joiner: DataJoiner, - keyFactory: KeyFactory -): StateFlow> { - fun streamer(key: Key): Flow> { - println("STREAMING FOR KEY $key") - return stream(StoreReadRequest.fresh(key)) - } +interface Pager, SO : StoreData.Single> { + val state: StateFlow> + fun load(key: K) + + companion object { + fun , K : StoreKey, SO : StoreData.Single, O : StoreData> create( + scope: CoroutineScope, + store: Store, + joiner: Joiner, + keyFactory: KeyFactory + ): Pager { + + val streamer = object : Streamer { + override fun invoke(key: K): Flow> { + return store.stream(StoreReadRequest.fresh(key)) + } + } + + return RealPager( + scope, + streamer, + joiner, + keyFactory + ) + } - val pager = RealStorePager(scope, ::streamer, joiner, keyFactory) + fun , K : StoreKey, SO : StoreData.Single, O : StoreData> create( + scope: CoroutineScope, + store: MutableStore, + joiner: Joiner, + keyFactory: KeyFactory + ): Pager { - val childScope = scope + Job() + val streamer = object : Streamer { + override fun invoke(key: K): Flow> { + return store.stream(StoreReadRequest.fresh(key)) + } + } - childScope.launch { - keys.collect { key -> - pager.load(key) + return RealPager( + scope, + streamer, + joiner, + keyFactory + ) } } +} - return pager.state +@ExperimentalStoreApi +interface Joiner, SO : StoreData.Single> { + suspend operator fun invoke(data: Map>): PagingData } @ExperimentalStoreApi -class RealStorePager, SingleKey : StoreKey.Single, Output : StoreData, Collection : StoreData.Collection, Single : StoreData.Single>( - private val scope: CoroutineScope, - private val streamer: (key: Key) -> Flow>, - private val joiner: DataJoiner, - private val keyFactory: KeyFactory -) : StorePager { - private val mutableStateFlow = MutableStateFlow>(StoreReadResponse.Initial) - override val state: StateFlow> = mutableStateFlow.asStateFlow() +interface Streamer, O : StoreData> { + operator fun invoke(key: K): Flow> +} - private val data: MutableMap?> = mutableMapOf() - private val streams: MutableMap = mutableMapOf() +@ExperimentalStoreApi +interface KeyFactory> { + fun createFor(id: Id): SK +} - private val dataMutex = Mutex() - private val streamsMutex = Mutex() +@ExperimentalStoreApi +class RealPager, K : StoreKey, SO : StoreData.Single, O : StoreData>( + private val scope: CoroutineScope, + private val streamer: Streamer, + private val joiner: Joiner, + private val keyFactory: KeyFactory +) : Pager { - override fun load(key: Key) { + private val mutableStateFlow = MutableStateFlow(emptyPagingData()) + override val state: StateFlow> = mutableStateFlow.asStateFlow() - println("HITTING0 $key") + private val allPagingData: MutableMap> = mutableMapOf() + private val allStreams: MutableMap = mutableMapOf() + private val mutexForAllPagingData = Mutex() + private val mutexForAllStreams = Mutex() + override fun load(key: K) { if (key !is StoreKey.Collection<*>) { - throw IllegalArgumentException("Invalid key type") + throw IllegalArgumentException("Invalid key type.") } val childScope = scope + Job() childScope.launch { - streamsMutex.withLock { - if (streams[key]?.isActive != true) { - data[key] = null + mutexForAllStreams.withLock { + if (allStreams[key]?.isActive != true) { + allPagingData[key] = emptyPagingData() - val nestedKeys = mutableListOf() + val childrenKeys = mutableListOf() - val job = launch { + val mainJob = launch { streamer(key).collect { response -> - when (response) { - is StoreReadResponse.Data -> { - println("HITTING1 $response") - (response as? StoreReadResponse.Data)?.let { - dataMutex.withLock { - data[key] = it - val joinedData = joiner(key, data) - (joinedData as? StoreReadResponse.Data)?.let { - mutableStateFlow.emit(it) - } - } - + if (response is StoreReadResponse.Data) { + (response as? StoreReadResponse.Data>)?.let { dataWithCollection -> - it.value.items.forEach { single -> - // TODO: Start stream for each single - // TODO: When change in single, update paging state - val singleKey = keyFactory.createSingleFor(single.id) - (singleKey as? Key)?.let { k -> - - val nestedJob = launch { - streamer(k).collect { singleResponse -> - when (singleResponse) { - is StoreReadResponse.Data -> { - println("HITTING NESTED $singleResponse") - (singleResponse as? StoreReadResponse.Data)?.let { - dataMutex.withLock { - data[key]?.value?.items?.let { items -> - val index = - items.indexOfFirst { it.id == single.id } - val updatedItems = items.toMutableList() - - if (updatedItems[index] != it.value) { - println("HITTING FOR ${it.value}") - updatedItems[index] = it.value - val updatedCollection = - data[key]!!.value.copyWith(updatedItems) as? Collection - - updatedCollection?.let { collection -> - data[key] = data[key]!!.copy(collection) - - val joinedData = joiner(key, data) - (joinedData as? StoreReadResponse.Data)?.let { - mutableStateFlow.emit(it) - } - } - } - - } + mutexForAllPagingData.withLock { + allPagingData[key] = pagingDataFrom(dataWithCollection.value.items) + val joinedData = joiner(allPagingData) + mutableStateFlow.value = joinedData + } - } - } - } + dataWithCollection.value.items.forEach { single -> - else -> {} - } - } + val childKey = keyFactory.createFor(single.id) + (childKey as? K)?.let { + val childJob = launch { + initStreamAndHandleSingle(single, childKey, key) + } - } + childrenKeys.add(childKey) - streams[k] = nestedJob - nestedKeys.add(k) + // TODO: This might result in a deadlock + mutexForAllStreams.withLock { + allStreams[childKey] = childJob } - } } } - - else -> { - println("HITTING $response") - mutableStateFlow.emit(response) - } } } } - streams[key] = job + allStreams[key] = mainJob - job.invokeOnCompletion { - nestedKeys.forEach { - streams[it]?.cancel() - streams.remove(it) + mainJob.invokeOnCompletion { + childrenKeys.forEach { childKey -> + allStreams[childKey]?.cancel() + allStreams.remove(childKey) } - streams[key]?.cancel() - streams.remove(key) + allStreams[key]?.cancel() + allStreams.remove(key) } } } } } -} + private suspend fun initStreamAndHandleSingle(single: SO, childKey: K, parentKey: K) { + streamer(childKey).collect { response -> + if (response is StoreReadResponse.Data) { + (response as? StoreReadResponse.Data)?.let { dataWithSingle -> + mutexForAllPagingData.withLock { + allPagingData[parentKey]?.items?.let { items -> + val indexOfSingle = items.indexOfFirst { it.id == single.id } + val updatedItems = items.toMutableList() + if (updatedItems[indexOfSingle] != dataWithSingle.value) { + updatedItems[indexOfSingle] = dataWithSingle.value + + val updatedPagingData = allPagingData[parentKey]!!.copy(updatedItems) + allPagingData[parentKey] = updatedPagingData + + val joinedData = joiner(allPagingData) + mutableStateFlow.value = joinedData + } + } + } + + } + } + } + } + private fun emptyPagingData() = PagingData(emptyList()) + private fun pagingDataFrom(items: List) = PagingData(items) + +} diff --git a/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/LaunchPagingStoreTests.kt b/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/LaunchPagingStoreTests.kt index e613a68a5..5e8346904 100644 --- a/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/LaunchPagingStoreTests.kt +++ b/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/LaunchPagingStoreTests.kt @@ -2,9 +2,6 @@ package org.mobilenativefoundation.store.paging5 import app.cash.turbine.test import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest @@ -13,23 +10,18 @@ import org.mobilenativefoundation.store.paging5.util.FakePostApi import org.mobilenativefoundation.store.paging5.util.FakePostDatabase import org.mobilenativefoundation.store.paging5.util.PostApi import org.mobilenativefoundation.store.paging5.util.PostData -import org.mobilenativefoundation.store.paging5.util.PostDataJoiner import org.mobilenativefoundation.store.paging5.util.PostDatabase +import org.mobilenativefoundation.store.paging5.util.PostJoiner import org.mobilenativefoundation.store.paging5.util.PostKey import org.mobilenativefoundation.store.paging5.util.PostKeyFactory -import org.mobilenativefoundation.store.paging5.util.PostPutRequestResult import org.mobilenativefoundation.store.paging5.util.PostStoreFactory import org.mobilenativefoundation.store.store5.MutableStore -import org.mobilenativefoundation.store.store5.StoreReadRequest -import org.mobilenativefoundation.store.store5.StoreReadResponse -import org.mobilenativefoundation.store.store5.StoreReadResponseOrigin import org.mobilenativefoundation.store.store5.StoreWriteRequest import kotlin.test.BeforeTest import kotlin.test.Test import kotlin.test.assertEquals -import kotlin.test.assertIs -@OptIn(ExperimentalStoreApi::class) +@OptIn(ExperimentalStoreApi::class, ExperimentalCoroutinesApi::class) class LaunchPagingStoreTests { private val testScope = TestScope() @@ -37,8 +29,9 @@ class LaunchPagingStoreTests { private lateinit var api: PostApi private lateinit var db: PostDatabase private lateinit var store: MutableStore - private lateinit var joiner: PostDataJoiner - private val keyFactory = PostKeyFactory() + private lateinit var joiner: PostJoiner + private lateinit var keyFactory: PostKeyFactory + private lateinit var pager: Pager @BeforeTest fun setup() { @@ -46,71 +39,58 @@ class LaunchPagingStoreTests { db = FakePostDatabase(userId) val factory = PostStoreFactory(api, db) store = factory.create() - joiner = PostDataJoiner() - } - - @Test - fun transitionFromInitialToData() = testScope.runTest { - val key = PostKey.Cursor("1", 10) - val keys = flowOf(key) - val stateFlow = store.launchPagingStore(this, keys, joiner, keyFactory = keyFactory) - - stateFlow.test { - val state1 = awaitItem() - assertIs(state1) - val state2 = awaitItem() - assertIs(state2) - val state3 = awaitItem() - assertIs>(state3) - expectNoEvents() - } + joiner = PostJoiner() + keyFactory = PostKeyFactory() } @Test fun multipleValidKeysEmittedInSuccession() = testScope.runTest { + pager = Pager.create(this, store, joiner, keyFactory) + val key1 = PostKey.Cursor("1", 10) val key2 = PostKey.Cursor("11", 10) - val keys = MutableStateFlow(key1) - val stateFlow = store.launchPagingStore(this, keys, joiner, keyFactory = keyFactory) + + val stateFlow = pager.state stateFlow.test { + pager.load(key1) + val initialState = awaitItem() + assertEquals(0, initialState.items.size) + val state1 = awaitItem() - assertIs(state1) - val state2 = awaitItem() - assertIs(state2) - val state3 = awaitItem() - assertIs>(state3) - assertEquals("1", state3.value.posts[0].postId) + assertEquals(10, state1.items.size) + assertEquals("1", state1.items[0].postId) - keys.emit(key2) + pager.load(key2) - val loading2 = awaitItem() - assertIs(loading2) + val state2 = awaitItem() + assertEquals(20, state2.items.size) + assertEquals("1", state2.items[0].postId) + assertEquals("11", state2.items[10].postId) - val state4 = awaitItem() - assertIs>(state4) - assertEquals("1", state4.value.posts[0].postId) - assertEquals("11", state4.value.posts[10].postId) - val data4 = state4.value - assertIs(data4) - assertEquals(20, data4.items.size) expectNoEvents() } } @Test fun sameKeyEmittedMultipleTimes() = testScope.runTest { + pager = Pager.create(this, store, joiner, keyFactory) + val key = PostKey.Cursor("1", 10) - val keys = flowOf(key, key) - val stateFlow = store.launchPagingStore(this, keys, joiner, keyFactory = keyFactory) + + val stateFlow = pager.state stateFlow.test { + pager.load(key) + val initialState = awaitItem() + assertEquals(0, initialState.items.size) + val state1 = awaitItem() - assertIs(state1) - val state2 = awaitItem() - assertIs(state2) - val state3 = awaitItem() - assertIs>(state3) + assertEquals(10, state1.items.size) + assertEquals("1", state1.items[0].postId) + + pager.load(key) + expectNoEvents() } } @@ -120,118 +100,38 @@ class LaunchPagingStoreTests { val api = FakePostApi() val db = FakePostDatabase(userId) val factory = PostStoreFactory(api = api, db = db) - val store = factory.create() + val mutableStore = factory.create() + + pager = Pager.create(this, mutableStore, joiner, keyFactory) val key1 = PostKey.Cursor("1", 10) val key2 = PostKey.Cursor("11", 10) - val keys = MutableStateFlow(key1) - - val stateFlow = store.launchPagingStore(this, keys, joiner, keyFactory = keyFactory) + val stateFlow = pager.state stateFlow.test { + pager.load(key1) val initialState = awaitItem() - assertIs(initialState) - val loadingState = awaitItem() - assertIs(loadingState) - val loadedState1 = awaitItem() - assertIs>(loadedState1) - val data1 = loadedState1.value - assertEquals(10, data1.posts.size) - assertEquals("1", data1.posts[0].postId) - expectNoEvents() + assertEquals(0, initialState.items.size) - keys.emit(key2) - - val loadingState2 = awaitItem() - assertIs(loadingState2) - val loadedState2 = awaitItem() - assertIs>(loadedState2) - val data2 = loadedState2.value - println(data2) - assertEquals(20, data2.posts.size) - assertEquals("1", data2.posts[0].postId) - } - - val cached = store.stream(StoreReadRequest.cached(key1, refresh = false)) - .first { it.dataOrNull() != null } - assertIs>(cached) - assertEquals(StoreReadResponseOrigin.Cache, cached.origin) - val data = cached.requireData() - assertIs(data) - assertEquals(10, data.posts.size) - - val cached2 = store.stream(StoreReadRequest.cached(PostKey.Single("2"), refresh = false)) - .first { it.dataOrNull() != null } - assertIs>(cached2) - assertEquals(StoreReadResponseOrigin.Cache, cached2.origin) - val data2 = cached2.requireData() - assertIs(data2) - assertEquals("2", data2.title) - - store.write(StoreWriteRequest.of(PostKey.Single("2"), PostData.Post("2", "2-modified"))) - - val cached3 = store.stream(StoreReadRequest.cached(PostKey.Single("2"), refresh = false)) - .first { it.dataOrNull() != null } - assertIs>(cached3) - assertEquals(StoreReadResponseOrigin.Cache, cached3.origin) - val data3 = cached3.requireData() - assertIs(data3) - assertEquals("2-modified", data3.title) - - val cached4 = - store.stream(StoreReadRequest.cached(PostKey.Cursor("1", 10), refresh = false)) - .first { it.dataOrNull() != null } - assertIs>(cached4) - assertEquals(StoreReadResponseOrigin.Cache, cached4.origin) - val data4 = cached4.requireData() - assertIs(data4) - assertEquals("2-modified", data4.posts[1].title) - } - - @OptIn(ExperimentalCoroutinesApi::class) - @Test - fun multipleKeysWithReadsAndWritesUsingOneStream() = testScope.runTest { - val api = FakePostApi() - val db = FakePostDatabase(userId) - val factory = PostStoreFactory(api = api, db = db) - val mutableStore = factory.create() + val state1 = awaitItem() + assertEquals(10, state1.items.size) + assertEquals("1", state1.items[0].postId) - val key1 = PostKey.Cursor("1", 10) - val key2 = PostKey.Cursor("11", 10) - val keys = MutableStateFlow(key1) + pager.load(key2) - val stateFlow = mutableStore.launchPagingStore(this, keys, joiner, keyFactory = keyFactory) - stateFlow.test { - val initialState = awaitItem() - assertIs(initialState) - val loadingState = awaitItem() - assertIs(loadingState) - val loadedState1 = awaitItem() - assertIs>(loadedState1) - val data1 = loadedState1.value - assertEquals(10, data1.posts.size) - assertEquals("1", data1.posts[0].postId) - - keys.emit(key2) - - val loadingState2 = awaitItem() - assertIs(loadingState2) - - val loadedState2 = awaitItem() - assertIs>(loadedState2) - val data2 = loadedState2.value - assertEquals(20, data2.posts.size) - assertEquals("1", data1.posts[0].postId) + val state2 = awaitItem() + assertEquals(20, state2.items.size) + assertEquals("1", state2.items[0].postId) + assertEquals("11", state2.items[10].postId) mutableStore.write(StoreWriteRequest.of(PostKey.Single("2"), PostData.Post("2", "2-modified"))) - println("WROTE TO STORE") advanceUntilIdle() - val loadedState3 = awaitItem() - assertIs>(loadedState3) - val data3 = loadedState3.value - assertEquals(20, data3.posts.size) - assertEquals("2-modified", data3.posts[1].title) // Actual is "2" + val state3 = awaitItem() + assertEquals(20, state3.items.size) + assertEquals("1", state3.items[0].postId) + assertEquals("2-modified", state3.items[1].title) + assertEquals("11", state3.items[10].postId) } } } diff --git a/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostDataJoiner.kt b/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostDataJoiner.kt deleted file mode 100644 index 2586ef15b..000000000 --- a/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostDataJoiner.kt +++ /dev/null @@ -1,30 +0,0 @@ -package org.mobilenativefoundation.store.paging5.util - -import org.mobilenativefoundation.store.core5.ExperimentalStoreApi -import org.mobilenativefoundation.store.paging5.DataJoiner -import org.mobilenativefoundation.store.store5.StoreReadResponse - -@OptIn(ExperimentalStoreApi::class) -class PostDataJoiner : DataJoiner { - override suspend fun invoke( - key: PostKey, - data: Map?> - ): StoreReadResponse.Data { - var combinedItems = mutableListOf() - - data.values.forEach { responseData -> - println("RESPONSE DATA = $responseData") - responseData?.value?.items?.let { items -> - combinedItems = (combinedItems + items).distinctBy { it.postId }.toMutableList() - } - } - - return (key as? PostKey.Cursor)?.let { - val feed = PostData.Feed(combinedItems) - data.values.last { it != null }?.let { - StoreReadResponse.Data(feed, it.origin) - } - } ?: throw IllegalArgumentException("Key must be a Collection type") - } - -} \ No newline at end of file diff --git a/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostJoiner.kt b/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostJoiner.kt new file mode 100644 index 000000000..e032be3cb --- /dev/null +++ b/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostJoiner.kt @@ -0,0 +1,20 @@ +package org.mobilenativefoundation.store.paging5.util + +import org.mobilenativefoundation.store.core5.ExperimentalStoreApi +import org.mobilenativefoundation.store.paging5.Joiner +import org.mobilenativefoundation.store.paging5.PagingData + +@OptIn(ExperimentalStoreApi::class) +class PostJoiner : Joiner { + override suspend fun invoke(data: Map>): PagingData { + var combinedItems = mutableListOf() + + data.values.forEach { responseData -> + responseData.items.let { items -> + combinedItems = (combinedItems + items).distinctBy { it.postId }.toMutableList() + } + } + + return PagingData(combinedItems) + } +} \ No newline at end of file diff --git a/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostKey.kt b/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostKey.kt index cff1cd117..04bf1285a 100644 --- a/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostKey.kt +++ b/paging/src/commonTest/kotlin/org/mobilenativefoundation/store/paging5/util/PostKey.kt @@ -2,9 +2,8 @@ package org.mobilenativefoundation.store.paging5.util import org.mobilenativefoundation.store.core5.ExperimentalStoreApi import org.mobilenativefoundation.store.core5.InsertionStrategy -import org.mobilenativefoundation.store.core5.KeyFactory -import org.mobilenativefoundation.store.core5.StoreData import org.mobilenativefoundation.store.core5.StoreKey +import org.mobilenativefoundation.store.paging5.KeyFactory @OptIn(ExperimentalStoreApi::class) sealed class PostKey : StoreKey { @@ -15,14 +14,16 @@ sealed class PostKey : StoreKey { override val filters: List>? = null, override val insertionStrategy: InsertionStrategy = InsertionStrategy.APPEND ) : StoreKey.Collection.Cursor, PostKey() + data class Single( override val id: String ) : StoreKey.Single, PostKey() } +@OptIn(ExperimentalStoreApi::class) class PostKeyFactory : KeyFactory { - override fun createSingleFor(id: String): PostKey.Single { + override fun createFor(id: String): PostKey.Single { return PostKey.Single(id) } } \ No newline at end of file