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

commonMain.com.seiko.imageloader.ImageLoader.kt Maven / Gradle / Ivy

The newest version!
package com.seiko.imageloader

import androidx.compose.runtime.Immutable
import androidx.compose.ui.geometry.isUnspecified
import com.seiko.imageloader.intercept.createInterceptorChain
import com.seiko.imageloader.model.ImageAction
import com.seiko.imageloader.model.ImageEvent
import com.seiko.imageloader.model.ImageRequest
import com.seiko.imageloader.model.ImageResult
import com.seiko.imageloader.option.Options
import com.seiko.imageloader.option.SizeResolver
import com.seiko.imageloader.util.ioDispatcher
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.withContext
import kotlinx.coroutines.withTimeoutOrNull
import kotlin.coroutines.CoroutineContext

@Immutable
interface ImageLoader {
    val config: ImageLoaderConfig

    fun async(request: ImageRequest): Flow

    suspend fun execute(request: ImageRequest): ImageResult

    companion object
}

fun ImageLoader(
    requestCoroutineContext: CoroutineContext = ioDispatcher,
    block: ImageLoaderConfigBuilder.() -> Unit,
): ImageLoader = RealImageLoader(
    requestCoroutineContext = requestCoroutineContext,
    config = ImageLoaderConfig(block),
)

@Immutable
private class RealImageLoader(
    private val requestCoroutineContext: CoroutineContext,
    override val config: ImageLoaderConfig,
) : ImageLoader {
    override fun async(request: ImageRequest) = flow {
        if (!request.skipEvent) {
            emit(ImageEvent.Start)
        }
        val chain = createInterceptorChain(
            initialRequest = request,
            initialOptions = getOptions(request),
            config = config,
            flowCollector = { emit(it) },
        )
        emit(chain.proceed(request))
    }.catch {
        if (it !is CancellationException) {
            emit(ImageResult.OfError(it))
        }
    }.flowOn(requestCoroutineContext)

    override suspend fun execute(request: ImageRequest): ImageResult {
        return withContext(requestCoroutineContext) {
            val chain = createInterceptorChain(
                initialRequest = request,
                initialOptions = getOptions(request),
                config = config,
                flowCollector = null,
            )
            chain.proceed(request)
        }
    }

    private suspend fun getOptions(request: ImageRequest): Options {
        if (request.sizeResolver == SizeResolver.Unspecified) {
            return config.defaultOptions
        }

        val initialSize = withTimeoutOrNull(200) {
            request.sizeResolver.size()
        } ?: return config.defaultOptions

        if (initialSize.isUnspecified) {
            return config.defaultOptions
        }

        return Options(config.defaultOptions) {
            size = initialSize
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy