ErrorsDispatcher.kt

package com.louisfn.somovie.ui.common.error

import androidx.annotation.AnyThread
import com.louisfn.somovie.core.common.annotation.DefaultDispatcher
import com.louisfn.somovie.core.common.extension.cancelChildren
import com.louisfn.somovie.ui.common.model.ImmutableList
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import javax.inject.Inject
import javax.inject.Singleton

interface ErrorsDispatcher {

    @AnyThread
    fun dispatch(throwable: Throwable)

    @AnyThread
    fun dispatch(message: String)

    @AnyThread
    fun cancel()

    @AnyThread
    fun errorChanges(): Flow<ImmutableList<Error>>
}

@Singleton
class DefaultErrorsDispatcher @Inject constructor(
    private val commonErrorMapper: CommonErrorMapper,
    @DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher,
) : ErrorsDispatcher {
    private val scope = CoroutineScope(defaultDispatcher)

    private val _errors = MutableStateFlow<ImmutableList<Error>>(ImmutableList())

    override fun errorChanges(): Flow<ImmutableList<Error>> = _errors

    override fun dispatch(throwable: Throwable) {
        dispatch(commonErrorMapper.map(throwable))
    }

    override fun dispatch(message: String) {
        dispatch(SimpleMessageError(message))
    }

    override fun cancel() {
        _errors.value = ImmutableList()
        scope.cancelChildren()
    }

    @AnyThread
    private fun dispatch(error: Error) =
        scope.launch {
            _errors.update {
                if (!it.contains(error)) {
                    ImmutableList(it + error)
                } else {
                    it
                }
            }

            delay(error.duration)

            _errors.update { ImmutableList(it - error) }
        }
}