TransformedLiveData.kt

package chat.rocket.android.util.livedata

import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlin.coroutines.CoroutineContext


class TransformedLiveData<Source, Output>(
    private val runContext: CoroutineContext = Dispatchers.IO,
    private val source: LiveData<Source>,
    private val transformation: (Source?) -> Output?
) : LiveData<Output>() {
    private var job: Job? = null

    private val observer = Observer<Source> { source ->
        job?.cancel()
        job = GlobalScope.launch(runContext) {
            transformation(source)?.let { transformed ->
                // Could have used postValue instead, but using the UI context I can guarantee that
                // a canceled job will never emit values.
                withContext(Dispatchers.Main) {
                    value = transformed
                }
            }
        }
    }

    override fun onActive() {
        source.observeForever(observer)
    }

    override fun onInactive() {
        job?.cancel()
        source.removeObserver(observer)
    }
}

fun <Source, Output> LiveData<Source>.transform(
    runContext: CoroutineContext = Dispatchers.IO,
    transformation: (Source?) -> Output?
) = TransformedLiveData(runContext, this, transformation)