All Downloads are FREE. Search and download functionalities are using the official Maven repository.

commonMain.com.paoapps.fifi.model.ModelHelper.kt Maven / Gradle / Ivy

Go to download

Kotlin Multiplatform Mobile framework for optimal code sharing between iOS and Android.

There is a newer version: 0.0.31
Show newest version
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