commonMain.com.paoapps.fifi.model.ModelHelper.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fifi-framework Show documentation
Show all versions of fifi-framework Show documentation
Kotlin Multiplatform Mobile framework for optimal code sharing between iOS and Android.
package com.paoapps.fifi.model
import com.paoapps.blockedcache.BlockedCache
import com.paoapps.blockedcache.BlockedCacheData
import com.paoapps.blockedcache.CacheResult
import com.paoapps.blockedcache.Fetch
import com.paoapps.blockedcache.FetcherResult
import com.paoapps.blockedcache.asCacheResult
import com.paoapps.fifi.api.ClientApi
import com.paoapps.fifi.di.API_STATE_FLOW_QUALIFIER
import com.paoapps.fifi.log.debug
import com.paoapps.fifi.model.datacontainer.DataContainer
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.datetime.Instant
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import kotlin.time.Duration
fun ModelHelper, Api>.withApiBlockedCacheFlow(
debugName: String? = null,
blockedCache: BlockedCache,
fetch: Fetch,
apiCall: suspend (api: Api, modelData: BlockedCacheData) -> FetcherResult,
predicate: (T, Instant) -> Boolean = { _, _ -> true },
condition: Flow = flowOf(true),
): Flow> = apiFlow.flatMapLatest { api ->
withBlockedCacheFlow(debugName ?: name, blockedCache, fetch, {
apiCall(api, it)
}, predicate, condition) { updatedData, _ ->
updatedData
}
}
class ModelHelper(
val name: String,
val modelDataContainer: DataContainer
): KoinComponent {
val apiFlow: StateFlow by inject(API_STATE_FLOW_QUALIFIER)
suspend fun api(): Api = apiFlow.first()
fun createApiCallFlow(
data: suspend (api: Api) -> FetcherResult,
): Flow> {
return apiFlow.flatMapLatest { api ->
flow> {
emit(CacheResult.Loading(null, 0L))
val apiResponse = data(api)
emit(apiResponse.asCacheResult())
}
}.distinctUntilChanged()
}
fun withApiBlockedCacheFlow(
debugName: String? = null,
blockedCache: BlockedCache,
fetch: Fetch,
apiCall: suspend (api: Api, modelData: ModelData) -> FetcherResult,
predicate: (T, Instant) -> Boolean = { _, _ -> true },
condition: Flow = flowOf(true),
processData: (responseData: BlockedCacheData, modelData: ModelData) -> ModelData
): Flow> = apiFlow.flatMapLatest { api ->
withBlockedCacheFlow(debugName, blockedCache, fetch, {
apiCall(api, it)
}, predicate, condition, processData)
}
fun withApiBlockedCacheFlow(
debugName: String? = null,
blockedCache: BlockedCache,
forceRefresh: Boolean,
forceRefreshDelay: Long? = null,
apiCall: suspend (api: Api, modelData: ModelData) -> FetcherResult,
predicate: (T, Instant) -> Boolean = { _, _ -> true },
condition: Flow = flowOf(true),
processData: (responseData: BlockedCacheData, modelData: ModelData) -> ModelData
): Flow> = apiFlow.flatMapLatest { api ->
withBlockedCacheFlow(debugName, blockedCache, forceRefresh, forceRefreshDelay, {
apiCall(api, it)
}, predicate, condition, processData)
}
fun withBlockedCacheFlow(
debugName: String? = null,
blockedCache: BlockedCache,
fetch: Fetch,
apiCall: suspend (modelData: ModelData) -> FetcherResult,
predicate: (T, Instant) -> Boolean = { _, _ -> true },
condition: Flow = flowOf(true),
processData: (responseData: BlockedCacheData, modelData: ModelData) -> ModelData
): Flow> =
withBlockedCacheFlow(
debugName,
blockedCache = blockedCache,
forceRefresh = fetch is Fetch.Force,
forceRefreshDelay = (fetch as? Fetch.Force)?.minimumDelay,
apiCall = apiCall,
predicate = { data, time -> predicate(data, time) && if (fetch is Fetch.Cache) !fetch.ignoreExpiration else true },
condition = condition.map { it && fetch !is Fetch.NoFetch },
processData = processData
)
fun withBlockedCacheFlow(
debugName: String? = null,
blockedCache: BlockedCache,
forceRefresh: Boolean,
forceRefreshDelay: Long? = null,
apiCall: suspend (modelData: ModelData) -> FetcherResult,
predicate: (T, Instant) -> Boolean = { _, _ -> true },
condition: Flow = flowOf(true),
processData: (responseData: BlockedCacheData, modelData: ModelData) -> ModelData
): Flow> =
withModelDataFlow(
debugName = debugName,
apiCall = { modelData, updateData ->
blockedCache.getData(
forceRefresh = forceRefresh,
forceRefreshDelay = forceRefreshDelay,
predicate = predicate,
condition = condition,
fetcher = { apiCall(modelData) },
updateData = updateData
)
},
processData = processData
)
fun withModelDataFlow(
debugName: String? = null,
apiCall: suspend (modelData: ModelData, processSuccessData: (D) -> Unit) -> Flow>,
processData: (responseData: D, modelData: ModelData) -> ModelData
): Flow> {
return flow {
val r = withDataFlow(
{ data, processSuccess ->
val flow = apiCall(data, processSuccess)
flow
},
{ responseData, data ->
val updatedData = processData(responseData, data)
if (updatedData != data) {
updatedData
} else {
null
}
}
)
r.collect {
emit(it)
}
}
}
private suspend fun withDataFlow(
apiCall: suspend (data: ModelData, processSuccessData: (D) -> Unit) -> Flow>,
processData: ((responseData: D, data: ModelData) -> ModelData?)? = null
): Flow> {
return flow {
modelDataContainer.data?.let { modelData ->
val response = apiCall(modelData) { successData ->
if (processData != null) {
modelDataContainer.data?.let {
val processedData = processData(successData, it)
if (processedData != null) {
modelDataContainer.data = processedData
}
}
}
}
response.collect { response ->
emit(response)
}
}
}
}
suspend fun withData(apiCall: suspend (modelData: ModelData) -> FetcherResult, processData: ((responseData: T, modelData: ModelData) -> ModelData)? = null): FetcherResult {
val modelData = modelDataContainer.data ?: return FetcherResult.Error.Message("No Data")
val response = apiCall(modelData)
if (response is FetcherResult.Data && processData != null) {
val successData = response.value
modelDataContainer.data?.let {
val processedData = processData(successData, it)
modelDataContainer.data = processedData
}
}
return response
}
fun createBlockCache(
duration: Duration,
expire: Duration?,
selector: (ModelData) -> BlockedCacheData?,
trigger: Flow = flowOf(Unit),
isDebugEnabled: Boolean = false
): BlockedCache {
return createBlockCache(
dataContainer = modelDataContainer,
duration = duration,
expire = expire,
selector = selector,
name = name,
trigger = trigger,
isDebugEnabled = isDebugEnabled
)
}
}
fun ModelHelper, Api>.createBlockCache(
duration: Duration,
expire: Duration?,
trigger: Flow = flowOf(Unit),
isDebugEnabled: Boolean = false
): BlockedCache {
return createBlockCache(
dataContainer = modelDataContainer,
duration = duration,
expire = expire,
selector = { it },
name = name,
trigger = trigger,
isDebugEnabled = isDebugEnabled
)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy