Skip to content

Commit

Permalink
Clean up
Browse files Browse the repository at this point in the history
Signed-off-by: mramotar_dbx <mramotar@dropbox.com>
  • Loading branch information
matt-ramotar committed Oct 18, 2023
1 parent 7492b1f commit 641b2f7
Show file tree
Hide file tree
Showing 11 changed files with 168 additions and 179 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.mobilenativefoundation.store.paging5

interface KeyProvider<Id : Any, Single : Identifiable.Single<Id>> {
interface KeyProvider<Id : Any, Single : StoreData.Single<Id>> {
fun from(key: StoreKey.Collection<Id>, value: Single): StoreKey.Single<Id>
fun from(key: StoreKey.Single<Id>, value: Single): StoreKey.Collection<Id>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
@file:Suppress("UNCHECKED_CAST")


package org.mobilenativefoundation.store.paging5


import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import org.mobilenativefoundation.store.store5.ExperimentalStoreApi
import org.mobilenativefoundation.store.store5.MutableStore
import org.mobilenativefoundation.store.store5.Store
import org.mobilenativefoundation.store.store5.impl.extensions.fresh


typealias LoadedCollection<Id> = StoreState.Loaded.Collection<Id, StoreData.Single<Id>, StoreData.Collection<Id, StoreData.Single<Id>>>


/**
* Initializes and returns a [StateFlow] that reflects the state of the Store, updating by a flow of provided keys.
* @param scope A [CoroutineScope].
* @param keys A flow of keys that dictate how the Store should be updated.
* @param fresh A lambda that invokes [Store.fresh].
* @return A read-only [StateFlow] reflecting the state of the Store.
*/
private fun <Id : Any, Key : StoreKey<Id>, Output : StoreData<Id>> launchStore(
scope: CoroutineScope,
keys: Flow<Key>,
fresh: suspend (currentState: StoreState<Id, Output>, key: Key) -> StoreState<Id, Output>
): StateFlow<StoreState<Id, Output>> {
val stateFlow = MutableStateFlow<StoreState<Id, Output>>(StoreState.Loading)

scope.launch {
keys.collect { key ->
val nextState = fresh(stateFlow.value, key)
stateFlow.emit(nextState)
}
}

return stateFlow.asStateFlow()
}

/**
* Initializes and returns a [StateFlow] that reflects the state of the [Store], updating by a flow of provided keys.
* @see [launchStore].
*/
fun <Id : Any, Key : StoreKey<Id>, Output : StoreData<Id>> Store<Key, Output>.launchStore(
scope: CoroutineScope,
keys: Flow<Key>,
): StateFlow<StoreState<Id, Output>> {
return launchStore(scope, keys) { currentState, key ->
this.freshAndInsertUpdatedItems(currentState, key)
}
}

/**
* Initializes and returns a [StateFlow] that reflects the state of the [Store], updating by a flow of provided keys.
* @see [launchStore].
*/
@OptIn(ExperimentalStoreApi::class)
fun <Id : Any, Key : StoreKey<Id>, Output : StoreData<Id>> MutableStore<Key, Output>.launchStore(
scope: CoroutineScope,
keys: Flow<Key>,
): StateFlow<StoreState<Id, Output>> {
return launchStore(scope, keys) { currentState, key ->
this.freshAndInsertUpdatedItems(currentState, key)
}
}


/**
* Updates the Store's state based on a provided key and a retrieval mechanism.
* @param currentState The current state of the Store.
* @param key The key that dictates how the state should be updated.
* @param get A lambda that defines how to retrieve data from the Store based on a key.
*/
private suspend fun <Id : Any, Key : StoreKey<Id>, Output : StoreData<Id>> freshAndInsertUpdatedItems(
currentState: StoreState<Id, Output>,
key: Key,
get: suspend (key: Key) -> Output,
): StoreState<Id, Output> {
return try {
if (key !is StoreKey.Collection<*>) throw IllegalArgumentException("Invalid key type")

val lastOutput = when (currentState) {
is StoreState.Loaded.Collection<*, *, *> -> (currentState as LoadedCollection<Id>).data
else -> null
}

val nextOutput = get(key) as StoreData.Collection<Id, StoreData.Single<Id>>

val output = (lastOutput?.insertItems(key.loadType, nextOutput.items) ?: nextOutput)
StoreState.Loaded.Collection(output) as StoreState<Id, Output>

} catch (error: Exception) {
StoreState.Error.Exception(error)
}
}

/**
* Updates the [Store]'s state based on a provided key.
* @see [freshAndInsertUpdatedItems].
*/
private suspend fun <Id : Any, Key : StoreKey<Id>, Output : StoreData<Id>> Store<Key, Output>.freshAndInsertUpdatedItems(
currentState: StoreState<Id, Output>,
key: Key
): StoreState<Id, Output> {
return freshAndInsertUpdatedItems(
currentState,
key
) {
this.fresh(it)
}
}

/**
* Updates the [MutableStore]'s state based on a provided key.
* @see [freshAndInsertUpdatedItems].
*/
@OptIn(ExperimentalStoreApi::class)
private suspend fun <Id : Any, Key : StoreKey<Id>, Output : StoreData<Id>> MutableStore<Key, Output>.freshAndInsertUpdatedItems(
currentState: StoreState<Id, Output>,
key: Key
): StoreState<Id, Output> {
return freshAndInsertUpdatedItems(
currentState,
key
) {
this.fresh<Key, Output, Any>(it)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import org.mobilenativefoundation.store.cache5.Cache
* Depends on [PagingCacheAccessor] for internal data management.
* @see [Cache].
*/
class PagingCache<Id : Any, Key : StoreKey<Id>, StoreOutput : Identifiable<Id>, Collection : Identifiable.Collection<Id, Single>, Single : Identifiable.Single<Id>>(
class PagingCache<Id : Any, Key : StoreKey<Id>, StoreOutput : StoreData<Id>, Collection : StoreData.Collection<Id, Single>, Single : StoreData.Single<Id>>(
private val keyProvider: KeyProvider<Id, Single>,
) : Cache<Key, StoreOutput> {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import org.mobilenativefoundation.store.cache5.CacheBuilder
* Intermediate data manager for a caching system supporting pagination.
* Tracks keys for rapid data retrieval and modification.
*/
class PagingCacheAccessor<Id : Any, Collection : Identifiable.Collection<Id, Single>, Single : Identifiable.Single<Id>> {
class PagingCacheAccessor<Id : Any, Collection : StoreData.Collection<Id, Single>, Single : StoreData.Single<Id>> {
private val collections = CacheBuilder<StoreKey.Collection<Id>, Collection>().build()
private val singles = CacheBuilder<StoreKey.Single<Id>, Single>().build()
private val keys = mutableSetOf<StoreKey<Id>>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ package org.mobilenativefoundation.store.paging5

/**
* An interface that defines items that can be uniquely identified.
* Every item that implements the [Identifiable] interface must have a means of identification.
* Every item that implements the [StoreData] interface must have a means of identification.
* This is useful in scenarios when data can be represented as singles or collections.
*/

interface Identifiable<out Id : Any> {
interface StoreData<out Id : Any> {

/**
* Represents a single identifiable item.
*/
interface Single<Id : Any> : Identifiable<Id> {
interface Single<Id : Any> : StoreData<Id> {
val id: Id
}

/**
* Represents a collection of identifiable items.
*/
interface Collection<Id : Any, S : Single<Id>> : Identifiable<Id> {
interface Collection<Id : Any, S : Single<Id>> : StoreData<Id> {
val items: List<S>

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package org.mobilenativefoundation.store.paging5
/**
* An interface that defines various states of data-fetching operations.
*/
sealed interface StoreState<out Id : Any, out Output : Identifiable<Id>> {
sealed interface StoreState<out Id : Any, out Output : StoreData<Id>> {

/**
* Represents the initial state.
Expand All @@ -19,17 +19,17 @@ sealed interface StoreState<out Id : Any, out Output : Identifiable<Id>> {
/**
* Represents successful fetch operations.
*/
sealed interface Loaded<Id : Any, Output : Identifiable<Id>> : StoreState<Id, Output> {
sealed interface Loaded<Id : Any, Output : StoreData<Id>> : StoreState<Id, Output> {

/**
* Represents a successful fetch of an individual item.
*/
data class Single<Id : Any, Output : Identifiable.Single<Id>>(val data: Output) : Loaded<Id, Output>
data class Single<Id : Any, Output : StoreData.Single<Id>>(val data: Output) : Loaded<Id, Output>

/**
* Represents a successful fetch of a collection of items.
*/
data class Collection<Id : Any, SO : Identifiable.Single<Id>, CO : Identifiable.Collection<Id, SO>>(val data: CO) :
data class Collection<Id : Any, SO : StoreData.Single<Id>, CO : StoreData.Collection<Id, SO>>(val data: CO) :
Loaded<Id, CO>
}

Expand Down

This file was deleted.

Loading

0 comments on commit 641b2f7

Please sign in to comment.