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

commonMain.com.paoapps.fifi.viewmodel.AbstractViewModel.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.viewmodel

import com.paoapps.blockedcache.Fetch
import com.paoapps.fifi.log.warn
import com.paoapps.fifi.utils.ActionHandler
import com.paoapps.fifi.utils.flow.FlowAdapter
import com.paoapps.fifi.utils.flow.FlowRefreshTrigger
import com.paoapps.fifi.utils.flow.wrap
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield
import org.koin.core.component.KoinComponent

abstract class AbstractEvent(val animate: Boolean = false)
object VoidEvent : AbstractEvent()

abstract class AbstractViewModel(): ViewModel(), KoinComponent {
    val actionHandler = ActionHandler(viewModelScope, ::_handleEvent)

    private val flowRefreshTrigger: MutableStateFlow = MutableStateFlow(
        FlowRefreshTrigger.FromCache()
    )

    open suspend fun _handleEvent(event: Event, input: Any?): ActionHandler.EventResult? {
        animateFlow.value = event.animate
        return handleEvent(event, input)
    }

    open suspend fun handleEvent(event: Event, input: Any?): ActionHandler.EventResult? {
        return handleEvent(event)
    }

    open suspend fun handleEvent(event: Event): ActionHandler.EventResult? {
        warn("Unhandled event: $event")
        return null
    }

    inner class OutputUpdate(
        val output: Output,
        val animate: Boolean = false
    )

    private val _outputState = MutableStateFlow(null as Output?)
    val outputState: StateFlow by lazy {
        if (output.flow is StateFlow) {
            output.flow as StateFlow
        } else {
            viewModelScope.launch {
                output.collect { _outputState.value = it }
            }
            _outputState
        }
    }

    abstract val output: FlowAdapter
    val animateFlow = MutableStateFlow(false)
    val viewModelOutput by lazy {
        combine(output, animateFlow) { output, animate ->
            OutputUpdate(output, animate)
        }.wrap(viewModelScope)
    }
    open val action: FlowAdapter = actionHandler.actions

    val notificationDismissEvent: Event? = null

    val confirmationDialogs = actionHandler.confirmationDialogs

    val globalActions = merge(actionHandler.globalActions).wrap(viewModelScope)

    open fun emitEvent(event: Event, input: Any? = null) {
        viewModelScope.launch {
            actionHandler.events.emit(Pair(event, input))
        }
    }

    open fun refresh() {
        viewModelScope.launch {
            yield()
            flowRefreshTrigger.value = FlowRefreshTrigger.Refresh()
        }
    }

    open suspend fun suspendRefresh() {
        refresh()
    }

    fun  createRefreshableFlow(data: suspend (refresh: Boolean) -> Flow): Flow {
        return flowRefreshTrigger.flatMapLatest { trigger ->
            val refresh = trigger is FlowRefreshTrigger.Refresh
            data(refresh)
        }
            .distinctUntilChanged()
            // need to share because otherwise when failure, it can cause a continuous loop of requests
            .shareIn(viewModelScope, started = SharingStarted.Lazily, replay = 1)
    }

    fun  createRefreshableFetchFlow(data: (refresh: Fetch) -> Flow) = createRefreshableFetchFlow(default = Fetch.Cache(), data = data)

    fun  createRefreshableFetchFlow(default: Fetch, data: (refresh: Fetch) -> Flow): Flow =
        createRefreshableFlow { data(if (it) Fetch.Force() else default) }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy