Switch from Kamel to Compose-ImageLoader

This commit is contained in:
Syer10
2022-08-24 20:03:16 -04:00
parent 7a68f9acfe
commit ddbf9d16b8
27 changed files with 384 additions and 89 deletions

View File

@@ -27,6 +27,7 @@ dependencies {
implementation(libs.accompanist.pagerIndicators) implementation(libs.accompanist.pagerIndicators)
implementation(libs.accompanist.flowLayout) implementation(libs.accompanist.flowLayout)
implementation(libs.kamel) implementation(libs.kamel)
implementation(libs.imageloader)
implementation(libs.materialDialogs.core) implementation(libs.materialDialogs.core)
// Android // Android

View File

@@ -37,6 +37,7 @@ dependencies {
implementation(libs.accompanist.pagerIndicators) implementation(libs.accompanist.pagerIndicators)
implementation(libs.accompanist.flowLayout) implementation(libs.accompanist.flowLayout)
implementation(libs.kamel) implementation(libs.kamel)
implementation(libs.imageloader)
implementation(libs.materialDialogs.core) implementation(libs.materialDialogs.core)
// UI (Swing) // UI (Swing)

View File

@@ -13,6 +13,7 @@ composeAndroid = "1.2.1"
voyager = "1.0.0-beta16" voyager = "1.0.0-beta16"
accompanist = "0.25.1" accompanist = "0.25.1"
kamel = "0.4.1" kamel = "0.4.1"
imageloader = "1.1.3"
materialDialogs = "0.8.0" materialDialogs = "0.8.0"
# Android # Android
@@ -101,6 +102,7 @@ accompanist-pager = { module = "ca.gosyer:accompanist-pager", version.ref = "acc
accompanist-pagerIndicators = { module = "ca.gosyer:accompanist-pager-indicators", version.ref = "accompanist" } accompanist-pagerIndicators = { module = "ca.gosyer:accompanist-pager-indicators", version.ref = "accompanist" }
accompanist-flowLayout = { module = "ca.gosyer:accompanist-flowlayout", version.ref = "accompanist" } accompanist-flowLayout = { module = "ca.gosyer:accompanist-flowlayout", version.ref = "accompanist" }
kamel = { module = "com.alialbaali.kamel:kamel-image", version.ref = "kamel" } kamel = { module = "com.alialbaali.kamel:kamel-image", version.ref = "kamel" }
imageloader = { module = "io.github.qdsfdhvh:image-loader", version.ref = "imageloader" }
materialDialogs-core = { module = "ca.gosyer:compose-material-dialogs-core", version.ref = "materialDialogs" } materialDialogs-core = { module = "ca.gosyer:compose-material-dialogs-core", version.ref = "materialDialogs" }
# Android # Android

View File

@@ -43,6 +43,7 @@ kotlin {
api(kotlin("stdlib-common")) api(kotlin("stdlib-common"))
api(libs.coroutines.core) api(libs.coroutines.core)
api(libs.kamel) api(libs.kamel)
api(libs.imageloader)
api(libs.voyager.core) api(libs.voyager.core)
api(libs.voyager.navigation) api(libs.voyager.navigation)
api(libs.voyager.transitions) api(libs.voyager.transitions)

View File

@@ -0,0 +1,31 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.jui.ui.base.image
import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.ImageLoaderBuilder
import com.seiko.imageloader.cache.disk.DiskCache
import com.seiko.imageloader.cache.disk.DiskCacheBuilder
import com.seiko.imageloader.cache.memory.MemoryCache
import com.seiko.imageloader.cache.memory.MemoryCacheBuilder
import okio.Path.Companion.toOkioPath
actual fun imageLoaderBuilder(contextWrapper: ContextWrapper): ImageLoaderBuilder {
return ImageLoaderBuilder(contextWrapper)
}
actual fun diskCache(contextWrapper: ContextWrapper): DiskCache {
return DiskCacheBuilder()
.directory(contextWrapper.cacheDir.toOkioPath() / "image_cache")
.maxSizeBytes(1024 * 1024 * 150) // 150 MB
.build()
}
actual fun memoryCache(contextWrapper: ContextWrapper): MemoryCache {
return MemoryCacheBuilder(contextWrapper)
.build()
}

View File

@@ -10,8 +10,11 @@ import androidx.compose.runtime.ProvidedValue
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf
import ca.gosyer.jui.core.di.AppScope import ca.gosyer.jui.core.di.AppScope
import ca.gosyer.jui.ui.ViewModelComponent import ca.gosyer.jui.ui.ViewModelComponent
import ca.gosyer.jui.ui.base.image.ImageLoaderProvider
import ca.gosyer.jui.ui.base.image.KamelConfigProvider import ca.gosyer.jui.ui.base.image.KamelConfigProvider
import ca.gosyer.jui.uicore.vm.ContextWrapper import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.ImageLoader
import com.seiko.imageloader.LocalImageLoader
import io.kamel.core.config.KamelConfig import io.kamel.core.config.KamelConfig
import io.kamel.core.config.KamelConfigBuilder import io.kamel.core.config.KamelConfigBuilder
import io.kamel.image.config.LocalKamelConfig import io.kamel.image.config.LocalKamelConfig
@@ -22,6 +25,8 @@ interface UiComponent {
val kamelConfig: KamelConfig val kamelConfig: KamelConfig
val imageLoader: ImageLoader
val contextWrapper: ContextWrapper val contextWrapper: ContextWrapper
val hooks: Array<ProvidedValue<out Any>> val hooks: Array<ProvidedValue<out Any>>
@@ -30,10 +35,15 @@ interface UiComponent {
@Provides @Provides
fun kamelConfigFactory(contextWrapper: ContextWrapper): KamelConfig = kamelConfigProvider.get { kamelPlatformHandler(contextWrapper) } fun kamelConfigFactory(contextWrapper: ContextWrapper): KamelConfig = kamelConfigProvider.get { kamelPlatformHandler(contextWrapper) }
@AppScope
@Provides
fun imageLoaderFactory(imageLoaderProvider: ImageLoaderProvider): ImageLoader = imageLoaderProvider.get()
@Provides @Provides
fun getHooks(viewModelComponent: ViewModelComponent) = arrayOf( fun getHooks(viewModelComponent: ViewModelComponent) = arrayOf(
LocalViewModels provides viewModelComponent, LocalViewModels provides viewModelComponent,
LocalKamelConfig provides kamelConfig LocalKamelConfig provides kamelConfig,
LocalImageLoader provides imageLoader
) )
} }

View File

@@ -0,0 +1,84 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.jui.ui.base.image
import ca.gosyer.jui.domain.extension.model.Extension
import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.domain.server.Http
import ca.gosyer.jui.domain.server.service.ServerPreferences
import ca.gosyer.jui.domain.source.model.Source
import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.ImageLoader
import com.seiko.imageloader.ImageLoaderBuilder
import com.seiko.imageloader.cache.disk.DiskCache
import com.seiko.imageloader.cache.memory.MemoryCache
import com.seiko.imageloader.component.mapper.Mapper
import com.seiko.imageloader.request.Options
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import me.tatarka.inject.annotations.Inject
class ImageLoaderProvider @Inject constructor(
private val http: Http,
serverPreferences: ServerPreferences,
private val context: ContextWrapper
) {
@OptIn(DelicateCoroutinesApi::class)
val serverUrl = serverPreferences.serverUrl().stateIn(GlobalScope)
fun get(): ImageLoader {
return imageLoaderBuilder(context).apply {
httpClient { http }
components {
add(MangaCoverMapper())
add(ExtensionIconMapper())
add(SourceIconMapper())
}
options(
Options(
config = Options.ImageConfig.HARDWARE
)
)
diskCache {
diskCache(context)
}
memoryCache {
memoryCache(context)
}
}.build()
}
inner class MangaCoverMapper : Mapper<String> {
override fun map(data: Any, options: Options): String? {
if (data !is Manga) return null
if (data.thumbnailUrl.isNullOrBlank()) return null
return serverUrl.value.toString() + data.thumbnailUrl
}
}
inner class ExtensionIconMapper : Mapper<String> {
override fun map(data: Any, options: Options): String? {
if (data !is Extension) return null
if (data.iconUrl.isBlank()) return null
return serverUrl.value.toString() + data.iconUrl
}
}
inner class SourceIconMapper : Mapper<String> {
override fun map(data: Any, options: Options): String? {
if (data !is Source) return null
if (data.iconUrl.isBlank()) return null
return serverUrl.value.toString() + data.iconUrl
}
}
}
expect fun imageLoaderBuilder(contextWrapper: ContextWrapper): ImageLoaderBuilder
expect fun diskCache(contextWrapper: ContextWrapper): DiskCache
expect fun memoryCache(contextWrapper: ContextWrapper): MemoryCache

View File

@@ -37,7 +37,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ca.gosyer.jui.domain.chapter.model.Chapter import ca.gosyer.jui.domain.chapter.model.Chapter
@@ -58,7 +57,6 @@ import ca.gosyer.jui.uicore.components.mangaAspectRatio
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import ca.gosyer.jui.uicore.resources.stringResource import ca.gosyer.jui.uicore.resources.stringResource
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun DownloadsScreenContent( fun DownloadsScreenContent(
@@ -128,7 +126,7 @@ fun DownloadsItem(
.padding(start = 16.dp, top = 8.dp, bottom = 8.dp) .padding(start = 16.dp, top = 8.dp, bottom = 8.dp)
.clip(MaterialTheme.shapes.medium) .clip(MaterialTheme.shapes.medium)
.clickable { onClickCover() }, .clickable { onClickCover() },
cover = lazyPainterResource(item.manga, filterQuality = FilterQuality.Medium), data = item.manga,
contentDescription = item.manga.title contentDescription = item.manga.title
) )
MangaListItemColumn( MangaListItemColumn(

View File

@@ -54,14 +54,13 @@ import ca.gosyer.jui.uicore.components.LoadingScreen
import ca.gosyer.jui.uicore.components.VerticalScrollbar import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import ca.gosyer.jui.uicore.resources.stringResource import ca.gosyer.jui.uicore.resources.stringResource
import com.vanpra.composematerialdialogs.MaterialDialog import com.vanpra.composematerialdialogs.MaterialDialog
import com.vanpra.composematerialdialogs.MaterialDialogState import com.vanpra.composematerialdialogs.MaterialDialogState
import com.vanpra.composematerialdialogs.listItemsMultiChoice import com.vanpra.composematerialdialogs.listItemsMultiChoice
import com.vanpra.composematerialdialogs.rememberMaterialDialogState import com.vanpra.composematerialdialogs.rememberMaterialDialogState
import com.vanpra.composematerialdialogs.title import com.vanpra.composematerialdialogs.title
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun ExtensionsScreenContent( fun ExtensionsScreenContent(
@@ -153,7 +152,12 @@ fun ExtensionItem(
Box(modifier = Modifier.fillMaxWidth().padding(end = 12.dp).height(50.dp).background(MaterialTheme.colors.background)) { Box(modifier = Modifier.fillMaxWidth().padding(end = 12.dp).height(50.dp).background(MaterialTheme.colors.background)) {
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Spacer(Modifier.width(4.dp)) Spacer(Modifier.width(4.dp))
KamelImage(lazyPainterResource(extension, filterQuality = FilterQuality.Medium), extension.name, Modifier.size(50.dp)) ImageLoaderImage(
data = extension,
contentDescription = extension.name,
modifier = Modifier.size(50.dp),
filterQuality = FilterQuality.Medium
)
Spacer(Modifier.width(8.dp)) Spacer(Modifier.width(8.dp))
Column { Column {
val title = buildAnnotatedString { val title = buildAnnotatedString {

View File

@@ -22,7 +22,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ca.gosyer.jui.domain.manga.model.Manga import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.uicore.components.MangaListItem import ca.gosyer.jui.uicore.components.MangaListItem
@@ -31,7 +30,6 @@ import ca.gosyer.jui.uicore.components.MangaListItemTitle
import ca.gosyer.jui.uicore.components.VerticalScrollbar import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun LibraryMangaList( fun LibraryMangaList(
@@ -81,7 +79,6 @@ private fun LibraryMangaListItem(
showLanguage: Boolean, showLanguage: Boolean,
showLocal: Boolean showLocal: Boolean
) { ) {
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
MangaListItem( MangaListItem(
modifier = modifier then Modifier modifier = modifier then Modifier
.requiredHeight(56.dp) .requiredHeight(56.dp)
@@ -91,7 +88,7 @@ private fun LibraryMangaListItem(
modifier = Modifier modifier = Modifier
.size(40.dp) .size(40.dp)
.clip(MaterialTheme.shapes.medium), .clip(MaterialTheme.shapes.medium),
cover = cover, data = manga,
contentDescription = manga.title contentDescription = manga.title
) )
MangaListItemTitle( MangaListItemTitle(

View File

@@ -35,8 +35,7 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.mangaAspectRatio import ca.gosyer.jui.uicore.components.mangaAspectRatio
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun LibraryMangaComfortableGrid( fun LibraryMangaComfortableGrid(
@@ -94,7 +93,6 @@ private fun LibraryMangaComfortableGridItem(
showLanguage: Boolean, showLanguage: Boolean,
showLocal: Boolean showLocal: Boolean
) { ) {
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
val fontStyle = LocalTextStyle.current.merge( val fontStyle = LocalTextStyle.current.merge(
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp) TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
) )
@@ -106,14 +104,15 @@ private fun LibraryMangaComfortableGridItem(
.clip(MaterialTheme.shapes.medium) then modifier .clip(MaterialTheme.shapes.medium) then modifier
) { ) {
Column { Column {
KamelImage( ImageLoaderImage(
cover, manga,
contentDescription = manga.title, contentDescription = manga.title,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.aspectRatio(mangaAspectRatio) .aspectRatio(mangaAspectRatio)
.clip(MaterialTheme.shapes.medium), .clip(MaterialTheme.shapes.medium),
contentScale = ContentScale.Crop contentScale = ContentScale.Crop,
filterQuality = FilterQuality.Medium
) )
Text( Text(
text = manga.title, text = manga.title,

View File

@@ -38,8 +38,7 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.mangaAspectRatio import ca.gosyer.jui.uicore.components.mangaAspectRatio
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import io.kamel.image.lazyPainterResource
expect fun Modifier.libraryMangaModifier( expect fun Modifier.libraryMangaModifier(
onClickManga: () -> Unit, onClickManga: () -> Unit,
@@ -102,7 +101,6 @@ private fun LibraryMangaCompactGridItem(
showLanguage: Boolean, showLanguage: Boolean,
showLocal: Boolean showLocal: Boolean
) { ) {
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
val fontStyle = LocalTextStyle.current.merge( val fontStyle = LocalTextStyle.current.merge(
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp) TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
) )
@@ -113,11 +111,12 @@ private fun LibraryMangaCompactGridItem(
.aspectRatio(mangaAspectRatio) .aspectRatio(mangaAspectRatio)
.clip(MaterialTheme.shapes.medium) then modifier .clip(MaterialTheme.shapes.medium) then modifier
) { ) {
KamelImage( ImageLoaderImage(
cover, manga,
manga.title, manga.title,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop contentScale = ContentScale.Crop,
filterQuality = FilterQuality.Medium
) )
Box(modifier = Modifier.fillMaxSize() then shadowGradient) Box(modifier = Modifier.fillMaxSize() then shadowGradient)
Text( Text(

View File

@@ -29,8 +29,7 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.mangaAspectRatio import ca.gosyer.jui.uicore.components.mangaAspectRatio
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun LibraryMangaCoverOnlyGrid( fun LibraryMangaCoverOnlyGrid(
@@ -88,19 +87,18 @@ private fun LibraryMangaCoverOnlyGridItem(
showLanguage: Boolean, showLanguage: Boolean,
showLocal: Boolean showLocal: Boolean
) { ) {
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
Box( Box(
modifier = Modifier.padding(4.dp) modifier = Modifier.padding(4.dp)
.fillMaxWidth() .fillMaxWidth()
.aspectRatio(mangaAspectRatio) .aspectRatio(mangaAspectRatio)
.clip(MaterialTheme.shapes.medium) then modifier .clip(MaterialTheme.shapes.medium) then modifier
) { ) {
KamelImage( ImageLoaderImage(
cover, manga,
contentDescription = manga.title, contentDescription = manga.title,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop contentScale = ContentScale.Crop,
filterQuality = FilterQuality.Medium
) )
LibraryMangaBadges( LibraryMangaBadges(
modifier = Modifier.padding(4.dp), modifier = Modifier.padding(4.dp),

View File

@@ -41,14 +41,13 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.mangaAspectRatio import ca.gosyer.jui.uicore.components.mangaAspectRatio
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import ca.gosyer.jui.uicore.resources.stringResource import ca.gosyer.jui.uicore.resources.stringResource
import com.google.accompanist.flowlayout.FlowRow import com.google.accompanist.flowlayout.FlowRow
import com.vanpra.composematerialdialogs.MaterialDialog import com.vanpra.composematerialdialogs.MaterialDialog
import com.vanpra.composematerialdialogs.MaterialDialogState import com.vanpra.composematerialdialogs.MaterialDialogState
import com.vanpra.composematerialdialogs.listItemsMultiChoice import com.vanpra.composematerialdialogs.listItemsMultiChoice
import com.vanpra.composematerialdialogs.title import com.vanpra.composematerialdialogs.title
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun MangaItem(manga: Manga) { fun MangaItem(manga: Manga) {
@@ -74,15 +73,16 @@ fun MangaItem(manga: Manga) {
@Composable @Composable
private fun Cover(manga: Manga, modifier: Modifier = Modifier) { private fun Cover(manga: Manga, modifier: Modifier = Modifier) {
KamelImage( ImageLoaderImage(
resource = lazyPainterResource(manga, filterQuality = FilterQuality.Medium), data = manga,
contentDescription = manga.title, contentDescription = manga.title,
modifier = modifier, modifier = modifier,
errorModifier = modifier then Modifier errorModifier = modifier then Modifier
.aspectRatio( .aspectRatio(
ratio = mangaAspectRatio, ratio = mangaAspectRatio,
matchHeightConstraintsFirst = true matchHeightConstraintsFirst = true
) ),
filterQuality = FilterQuality.Medium
) )
} }

View File

@@ -37,8 +37,7 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.mangaAspectRatio import ca.gosyer.jui.uicore.components.mangaAspectRatio
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun SourceMangaComfortableGrid( fun SourceMangaComfortableGrid(
@@ -89,7 +88,6 @@ private fun SourceMangaComfortableGridItem(
manga: Manga, manga: Manga,
inLibrary: Boolean inLibrary: Boolean
) { ) {
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
val fontStyle = LocalTextStyle.current.merge( val fontStyle = LocalTextStyle.current.merge(
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp) TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
) )
@@ -101,14 +99,15 @@ private fun SourceMangaComfortableGridItem(
.clip(MaterialTheme.shapes.medium) then modifier .clip(MaterialTheme.shapes.medium) then modifier
) { ) {
Column { Column {
KamelImage( ImageLoaderImage(
cover, manga,
contentDescription = manga.title, contentDescription = manga.title,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.aspectRatio(mangaAspectRatio) .aspectRatio(mangaAspectRatio)
.clip(MaterialTheme.shapes.medium), .clip(MaterialTheme.shapes.medium),
contentScale = ContentScale.Crop contentScale = ContentScale.Crop,
filterQuality = FilterQuality.Medium
) )
Text( Text(
text = manga.title, text = manga.title,

View File

@@ -40,8 +40,7 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.mangaAspectRatio import ca.gosyer.jui.uicore.components.mangaAspectRatio
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun SourceMangaCompactGrid( fun SourceMangaCompactGrid(
@@ -92,7 +91,6 @@ private fun SourceMangaCompactGridItem(
manga: Manga, manga: Manga,
inLibrary: Boolean inLibrary: Boolean
) { ) {
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
val fontStyle = LocalTextStyle.current.merge( val fontStyle = LocalTextStyle.current.merge(
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp) TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
) )
@@ -103,11 +101,12 @@ private fun SourceMangaCompactGridItem(
.aspectRatio(mangaAspectRatio) .aspectRatio(mangaAspectRatio)
.clip(MaterialTheme.shapes.medium) then modifier .clip(MaterialTheme.shapes.medium) then modifier
) { ) {
KamelImage( ImageLoaderImage(
cover, manga,
manga.title, manga.title,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop contentScale = ContentScale.Crop,
filterQuality = FilterQuality.Medium
) )
Box(modifier = Modifier.fillMaxSize().then(shadowGradient)) Box(modifier = Modifier.fillMaxSize().then(shadowGradient))
Text( Text(

View File

@@ -22,7 +22,6 @@ import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ca.gosyer.jui.domain.manga.model.Manga import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.uicore.components.MangaListItem import ca.gosyer.jui.uicore.components.MangaListItem
@@ -31,7 +30,6 @@ import ca.gosyer.jui.uicore.components.MangaListItemTitle
import ca.gosyer.jui.uicore.components.VerticalScrollbar import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun SourceMangaList( fun SourceMangaList(
@@ -74,7 +72,6 @@ private fun MangaListItem(
manga: Manga, manga: Manga,
inLibrary: Boolean inLibrary: Boolean
) { ) {
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
MangaListItem( MangaListItem(
modifier = modifier then Modifier modifier = modifier then Modifier
.requiredHeight(56.dp) .requiredHeight(56.dp)
@@ -84,7 +81,7 @@ private fun MangaListItem(
modifier = Modifier modifier = Modifier
.size(40.dp) .size(40.dp)
.clip(MaterialTheme.shapes.medium), .clip(MaterialTheme.shapes.medium),
cover = cover, data = manga,
contentDescription = manga.title contentDescription = manga.title
) )
MangaListItemTitle( MangaListItemTitle(

View File

@@ -40,9 +40,8 @@ import ca.gosyer.jui.ui.sources.home.SourceHomeScreen
import ca.gosyer.jui.uicore.components.VerticalScrollbar import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import ca.gosyer.jui.uicore.resources.stringResource import ca.gosyer.jui.uicore.resources.stringResource
import io.kamel.image.lazyPainterResource
expect fun Modifier.sourceSideMenuItem( expect fun Modifier.sourceSideMenuItem(
onSourceTabClick: () -> Unit, onSourceTabClick: () -> Unit,
@@ -140,10 +139,11 @@ fun SourcesSideMenu(
modifier = modifier modifier = modifier
) )
is SourceNavigatorScreen.SourceScreen -> Box(Modifier.align(Alignment.Center)) { is SourceNavigatorScreen.SourceScreen -> Box(Modifier.align(Alignment.Center)) {
KamelImage( ImageLoaderImage(
resource = lazyPainterResource(screen.source, filterQuality = FilterQuality.Medium), data = screen.source,
contentDescription = screen.source.displayName, contentDescription = screen.source.displayName,
modifier = modifier modifier = modifier,
filterQuality = FilterQuality.Medium
) )
} }
} }

View File

@@ -28,8 +28,7 @@ import androidx.compose.ui.unit.times
import ca.gosyer.jui.domain.manga.model.Manga import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.ui.sources.browse.components.SourceMangaBadges import ca.gosyer.jui.ui.sources.browse.components.SourceMangaBadges
import ca.gosyer.jui.uicore.components.mangaAspectRatio import ca.gosyer.jui.uicore.components.mangaAspectRatio
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun GlobalSearchMangaComfortableGridItem( fun GlobalSearchMangaComfortableGridItem(
@@ -37,7 +36,6 @@ fun GlobalSearchMangaComfortableGridItem(
manga: Manga, manga: Manga,
inLibrary: Boolean inLibrary: Boolean
) { ) {
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
val fontStyle = LocalTextStyle.current.merge( val fontStyle = LocalTextStyle.current.merge(
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp) TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
) )
@@ -49,14 +47,15 @@ fun GlobalSearchMangaComfortableGridItem(
.clip(MaterialTheme.shapes.medium) then modifier .clip(MaterialTheme.shapes.medium) then modifier
) { ) {
Column { Column {
KamelImage( ImageLoaderImage(
cover, manga,
contentDescription = manga.title, contentDescription = manga.title,
modifier = Modifier modifier = Modifier
.height(200.dp) .height(200.dp)
.aspectRatio(mangaAspectRatio, true) .aspectRatio(mangaAspectRatio, true)
.clip(MaterialTheme.shapes.medium), .clip(MaterialTheme.shapes.medium),
contentScale = ContentScale.Crop contentScale = ContentScale.Crop,
filterQuality = FilterQuality.Medium
) )
Text( Text(
text = manga.title, text = manga.title,

View File

@@ -31,8 +31,7 @@ import androidx.compose.ui.unit.sp
import ca.gosyer.jui.domain.manga.model.Manga import ca.gosyer.jui.domain.manga.model.Manga
import ca.gosyer.jui.ui.sources.browse.components.SourceMangaBadges import ca.gosyer.jui.ui.sources.browse.components.SourceMangaBadges
import ca.gosyer.jui.uicore.components.mangaAspectRatio import ca.gosyer.jui.uicore.components.mangaAspectRatio
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun GlobalSearchMangaCompactGridItem( fun GlobalSearchMangaCompactGridItem(
@@ -40,7 +39,6 @@ fun GlobalSearchMangaCompactGridItem(
manga: Manga, manga: Manga,
inLibrary: Boolean inLibrary: Boolean
) { ) {
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
val fontStyle = LocalTextStyle.current.merge( val fontStyle = LocalTextStyle.current.merge(
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp) TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
) )
@@ -51,11 +49,12 @@ fun GlobalSearchMangaCompactGridItem(
.aspectRatio(mangaAspectRatio, true) .aspectRatio(mangaAspectRatio, true)
.clip(MaterialTheme.shapes.medium) then modifier .clip(MaterialTheme.shapes.medium) then modifier
) { ) {
KamelImage( ImageLoaderImage(
cover, manga,
manga.title, manga.title,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop contentScale = ContentScale.Crop,
filterQuality = FilterQuality.Medium
) )
Box(modifier = Modifier.fillMaxSize().then(shadowGradient)) Box(modifier = Modifier.fillMaxSize().then(shadowGradient))
Text( Text(

View File

@@ -56,10 +56,9 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import ca.gosyer.jui.uicore.resources.stringResource import ca.gosyer.jui.uicore.resources.stringResource
import com.vanpra.composematerialdialogs.rememberMaterialDialogState import com.vanpra.composematerialdialogs.rememberMaterialDialogState
import io.kamel.image.lazyPainterResource
@Composable @Composable
fun SourceHomeScreenContent( fun SourceHomeScreenContent(
@@ -175,7 +174,12 @@ fun WideSourceItem(
}, },
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
KamelImage(lazyPainterResource(source, filterQuality = FilterQuality.Medium), source.displayName, Modifier.size(96.dp)) ImageLoaderImage(
data = source,
contentDescription = source.displayName,
modifier = Modifier.size(96.dp),
filterQuality = FilterQuality.Medium
)
Spacer(Modifier.height(4.dp)) Spacer(Modifier.height(4.dp))
Text( Text(
"${source.name} (${source.displayLang.toUpperCase(Locale.current)})", "${source.name} (${source.displayLang.toUpperCase(Locale.current)})",
@@ -199,11 +203,12 @@ fun ThinSourceItem(
.padding(horizontal = 16.dp, vertical = 8.dp), .padding(horizontal = 16.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
KamelImage( ImageLoaderImage(
lazyPainterResource(source, filterQuality = FilterQuality.Medium), source,
source.displayName, source.displayName,
Modifier.fillMaxHeight() Modifier.fillMaxHeight()
.aspectRatio(1F, true) .aspectRatio(1F, true),
filterQuality = FilterQuality.Medium
) )
Spacer(Modifier.width(8.dp)) Spacer(Modifier.width(8.dp))
Column { Column {

View File

@@ -26,7 +26,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ca.gosyer.jui.domain.chapter.model.Chapter import ca.gosyer.jui.domain.chapter.model.Chapter
@@ -45,7 +44,6 @@ import ca.gosyer.jui.uicore.components.mangaAspectRatio
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
import ca.gosyer.jui.uicore.components.scrollbarPadding import ca.gosyer.jui.uicore.components.scrollbarPadding
import ca.gosyer.jui.uicore.resources.stringResource import ca.gosyer.jui.uicore.resources.stringResource
import io.kamel.image.lazyPainterResource
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
@Composable @Composable
@@ -137,7 +135,7 @@ fun UpdatesItem(
.padding(start = 16.dp, top = 8.dp, bottom = 8.dp) .padding(start = 16.dp, top = 8.dp, bottom = 8.dp)
.clip(MaterialTheme.shapes.medium) .clip(MaterialTheme.shapes.medium)
.clickable { onClickCover() }, .clickable { onClickCover() },
cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium), data = manga,
contentDescription = manga.title contentDescription = manga.title
) )
MangaListItemColumn( MangaListItemColumn(

View File

@@ -0,0 +1,31 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.jui.ui.base.image
import ca.gosyer.jui.core.io.userDataDir
import ca.gosyer.jui.uicore.vm.ContextWrapper
import com.seiko.imageloader.ImageLoaderBuilder
import com.seiko.imageloader.cache.disk.DiskCache
import com.seiko.imageloader.cache.disk.DiskCacheBuilder
import com.seiko.imageloader.cache.memory.MemoryCache
import com.seiko.imageloader.cache.memory.MemoryCacheBuilder
actual fun imageLoaderBuilder(contextWrapper: ContextWrapper): ImageLoaderBuilder {
return ImageLoaderBuilder()
}
actual fun diskCache(contextWrapper: ContextWrapper): DiskCache {
return DiskCacheBuilder()
.directory(userDataDir / "image_cache")
.maxSizeBytes(1024 * 1024 * 150) // 150 MB
.build()
}
actual fun memoryCache(contextWrapper: ContextWrapper): MemoryCache {
return MemoryCacheBuilder()
.build()
}

View File

@@ -39,6 +39,7 @@ kotlin {
api(kotlin("stdlib-common")) api(kotlin("stdlib-common"))
api(libs.coroutines.core) api(libs.coroutines.core)
api(libs.kamel) api(libs.kamel)
api(libs.imageloader)
api(libs.voyager.core) api(libs.voyager.core)
api(libs.dateTime) api(libs.dateTime)
api(projects.core) api(projects.core)

View File

@@ -23,20 +23,19 @@ import androidx.compose.ui.draw.drawWithCache
import androidx.compose.ui.geometry.Offset import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import io.kamel.core.Resource
@Composable @Composable
fun MangaGridItem( fun MangaGridItem(
title: String, title: String,
cover: Resource<Painter>, data: Any,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) {
val fontStyle = LocalTextStyle.current.merge( val fontStyle = LocalTextStyle.current.merge(
@@ -53,7 +52,7 @@ fun MangaGridItem(
shape = RoundedCornerShape(4.dp) shape = RoundedCornerShape(4.dp)
) { ) {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
KamelImage(cover, title, contentScale = ContentScale.Crop) ImageLoaderImage(data, title, contentScale = ContentScale.Crop, filterQuality = FilterQuality.Medium)
Box(modifier = Modifier.fillMaxSize().then(shadowGradient)) Box(modifier = Modifier.fillMaxSize().then(shadowGradient))
Text( Text(
text = title, text = title,

View File

@@ -15,12 +15,11 @@ import androidx.compose.material.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import ca.gosyer.jui.uicore.image.KamelImage import ca.gosyer.jui.uicore.image.ImageLoaderImage
import io.kamel.core.Resource
@Composable @Composable
fun MangaListItem( fun MangaListItem(
@@ -38,14 +37,15 @@ fun MangaListItem(
@Composable @Composable
fun MangaListItemImage( fun MangaListItemImage(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
cover: Resource<Painter>, data: Any,
contentDescription: String contentDescription: String
) { ) {
KamelImage( ImageLoaderImage(
cover, data,
contentDescription = contentDescription, contentDescription = contentDescription,
modifier = modifier, modifier = modifier,
contentScale = ContentScale.Crop contentScale = ContentScale.Crop,
filterQuality = FilterQuality.Medium
) )
} }

View File

@@ -0,0 +1,143 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.jui.uicore.image
import androidx.compose.animation.Crossfade
import androidx.compose.animation.core.FiniteAnimationSpec
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.BrokenImage
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.drawscope.DrawScope.Companion.DefaultFilterQuality
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import ca.gosyer.jui.uicore.components.LoadingScreen
import com.seiko.imageloader.ImageRequestState
import com.seiko.imageloader.rememberAsyncImagePainter
import com.seiko.imageloader.request.ImageRequestBuilder
import org.lighthousegames.logging.logging
private val log = logging()
private enum class ImageLoaderImageState {
Loading,
Success,
Failure,
}
@Composable
fun ImageLoaderImage(
data: Any,
contentDescription: String?,
modifier: Modifier = Modifier,
errorModifier: Modifier = modifier,
alignment: Alignment = Alignment.Center,
contentScale: ContentScale = ContentScale.Fit,
alpha: Float = DefaultAlpha,
colorFilter: ColorFilter? = null,
filterQuality: FilterQuality = DefaultFilterQuality,
onLoading: (@Composable BoxScope.(Float) -> Unit)? = {
LoadingScreen(progress = it.coerceIn(0.0F, 1.0F), modifier = modifier then Modifier.fillMaxSize())
},
onFailure: (@Composable BoxScope.(Throwable) -> Unit)? = {
LaunchedEffect(it) {
log.warn(it) { "Error loading image" }
}
Box(
modifier = errorModifier then Modifier.fillMaxSize()
.background(Color(0x1F888888)),
contentAlignment = Alignment.Center
) {
Icon(
Icons.Rounded.BrokenImage,
contentDescription = null,
tint = Color(0x1F888888),
modifier = Modifier.size(24.dp)
)
}
},
contentAlignment: Alignment = Alignment.Center,
animationSpec: FiniteAnimationSpec<Float>? = tween()
) {
val request = remember(data) { ImageRequestBuilder().data(data).build() }
val painter = rememberAsyncImagePainter(request, contentScale = contentScale)
val progress = remember { mutableStateOf(-1F) }
val error = remember { mutableStateOf<Throwable?>(null) }
val state by derivedStateOf {
when (val state = painter.requestState) {
is ImageRequestState.Failure -> {
progress.value = 0.0F
error.value = state.error
ImageLoaderImageState.Failure
}
ImageRequestState.Loading -> {
progress.value = 0.0F
ImageLoaderImageState.Loading
}
ImageRequestState.Success -> {
progress.value = 1.0F
ImageLoaderImageState.Success
}
}
}
if (animationSpec != null) {
Crossfade(state, animationSpec = animationSpec, modifier = modifier) {
Box(Modifier.fillMaxSize(), contentAlignment) {
when (it) {
ImageLoaderImageState.Loading -> if (onLoading != null) {
onLoading(progress.value)
}
ImageLoaderImageState.Success -> Image(
painter = painter,
contentDescription = contentDescription,
modifier = Modifier.fillMaxSize(),
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter
)
ImageLoaderImageState.Failure -> {
if (onFailure != null) {
onFailure(error.value ?: return@Crossfade)
}
}
}
}
}
} else {
Box(modifier, contentAlignment) {
Image(
painter = painter,
contentDescription = contentDescription,
modifier = Modifier.fillMaxSize(),
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter
)
}
}
}