mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Switch from Kamel to Compose-ImageLoader
This commit is contained in:
@@ -27,6 +27,7 @@ dependencies {
|
||||
implementation(libs.accompanist.pagerIndicators)
|
||||
implementation(libs.accompanist.flowLayout)
|
||||
implementation(libs.kamel)
|
||||
implementation(libs.imageloader)
|
||||
implementation(libs.materialDialogs.core)
|
||||
|
||||
// Android
|
||||
|
||||
@@ -37,6 +37,7 @@ dependencies {
|
||||
implementation(libs.accompanist.pagerIndicators)
|
||||
implementation(libs.accompanist.flowLayout)
|
||||
implementation(libs.kamel)
|
||||
implementation(libs.imageloader)
|
||||
implementation(libs.materialDialogs.core)
|
||||
|
||||
// UI (Swing)
|
||||
|
||||
@@ -13,6 +13,7 @@ composeAndroid = "1.2.1"
|
||||
voyager = "1.0.0-beta16"
|
||||
accompanist = "0.25.1"
|
||||
kamel = "0.4.1"
|
||||
imageloader = "1.1.3"
|
||||
materialDialogs = "0.8.0"
|
||||
|
||||
# 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-flowLayout = { module = "ca.gosyer:accompanist-flowlayout", version.ref = "accompanist" }
|
||||
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" }
|
||||
|
||||
# Android
|
||||
|
||||
@@ -43,6 +43,7 @@ kotlin {
|
||||
api(kotlin("stdlib-common"))
|
||||
api(libs.coroutines.core)
|
||||
api(libs.kamel)
|
||||
api(libs.imageloader)
|
||||
api(libs.voyager.core)
|
||||
api(libs.voyager.navigation)
|
||||
api(libs.voyager.transitions)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -10,8 +10,11 @@ import androidx.compose.runtime.ProvidedValue
|
||||
import androidx.compose.runtime.compositionLocalOf
|
||||
import ca.gosyer.jui.core.di.AppScope
|
||||
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.uicore.vm.ContextWrapper
|
||||
import com.seiko.imageloader.ImageLoader
|
||||
import com.seiko.imageloader.LocalImageLoader
|
||||
import io.kamel.core.config.KamelConfig
|
||||
import io.kamel.core.config.KamelConfigBuilder
|
||||
import io.kamel.image.config.LocalKamelConfig
|
||||
@@ -22,6 +25,8 @@ interface UiComponent {
|
||||
|
||||
val kamelConfig: KamelConfig
|
||||
|
||||
val imageLoader: ImageLoader
|
||||
|
||||
val contextWrapper: ContextWrapper
|
||||
|
||||
val hooks: Array<ProvidedValue<out Any>>
|
||||
@@ -30,10 +35,15 @@ interface UiComponent {
|
||||
@Provides
|
||||
fun kamelConfigFactory(contextWrapper: ContextWrapper): KamelConfig = kamelConfigProvider.get { kamelPlatformHandler(contextWrapper) }
|
||||
|
||||
@AppScope
|
||||
@Provides
|
||||
fun imageLoaderFactory(imageLoaderProvider: ImageLoaderProvider): ImageLoader = imageLoaderProvider.get()
|
||||
|
||||
@Provides
|
||||
fun getHooks(viewModelComponent: ViewModelComponent) = arrayOf(
|
||||
LocalViewModels provides viewModelComponent,
|
||||
LocalKamelConfig provides kamelConfig
|
||||
LocalKamelConfig provides kamelConfig,
|
||||
LocalImageLoader provides imageLoader
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -37,7 +37,6 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
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.scrollbarPadding
|
||||
import ca.gosyer.jui.uicore.resources.stringResource
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun DownloadsScreenContent(
|
||||
@@ -128,7 +126,7 @@ fun DownloadsItem(
|
||||
.padding(start = 16.dp, top = 8.dp, bottom = 8.dp)
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.clickable { onClickCover() },
|
||||
cover = lazyPainterResource(item.manga, filterQuality = FilterQuality.Medium),
|
||||
data = item.manga,
|
||||
contentDescription = item.manga.title
|
||||
)
|
||||
MangaListItemColumn(
|
||||
|
||||
@@ -54,14 +54,13 @@ import ca.gosyer.jui.uicore.components.LoadingScreen
|
||||
import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
|
||||
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 com.vanpra.composematerialdialogs.MaterialDialog
|
||||
import com.vanpra.composematerialdialogs.MaterialDialogState
|
||||
import com.vanpra.composematerialdialogs.listItemsMultiChoice
|
||||
import com.vanpra.composematerialdialogs.rememberMaterialDialogState
|
||||
import com.vanpra.composematerialdialogs.title
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun ExtensionsScreenContent(
|
||||
@@ -153,7 +152,12 @@ fun ExtensionItem(
|
||||
Box(modifier = Modifier.fillMaxWidth().padding(end = 12.dp).height(50.dp).background(MaterialTheme.colors.background)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
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))
|
||||
Column {
|
||||
val title = buildAnnotatedString {
|
||||
|
||||
@@ -22,7 +22,6 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
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.rememberScrollbarAdapter
|
||||
import ca.gosyer.jui.uicore.components.scrollbarPadding
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaList(
|
||||
@@ -81,7 +79,6 @@ private fun LibraryMangaListItem(
|
||||
showLanguage: Boolean,
|
||||
showLocal: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
MangaListItem(
|
||||
modifier = modifier then Modifier
|
||||
.requiredHeight(56.dp)
|
||||
@@ -91,7 +88,7 @@ private fun LibraryMangaListItem(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(MaterialTheme.shapes.medium),
|
||||
cover = cover,
|
||||
data = manga,
|
||||
contentDescription = manga.title
|
||||
)
|
||||
MangaListItemTitle(
|
||||
|
||||
@@ -35,8 +35,7 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
|
||||
import ca.gosyer.jui.uicore.components.scrollbarPadding
|
||||
import ca.gosyer.jui.uicore.image.KamelImage
|
||||
import io.kamel.image.lazyPainterResource
|
||||
import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaComfortableGrid(
|
||||
@@ -94,7 +93,6 @@ private fun LibraryMangaComfortableGridItem(
|
||||
showLanguage: Boolean,
|
||||
showLocal: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
@@ -106,14 +104,15 @@ private fun LibraryMangaComfortableGridItem(
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
Column {
|
||||
KamelImage(
|
||||
cover,
|
||||
ImageLoaderImage(
|
||||
manga,
|
||||
contentDescription = manga.title,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(mangaAspectRatio)
|
||||
.clip(MaterialTheme.shapes.medium),
|
||||
contentScale = ContentScale.Crop
|
||||
contentScale = ContentScale.Crop,
|
||||
filterQuality = FilterQuality.Medium
|
||||
)
|
||||
Text(
|
||||
text = manga.title,
|
||||
|
||||
@@ -38,8 +38,7 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
|
||||
import ca.gosyer.jui.uicore.components.scrollbarPadding
|
||||
import ca.gosyer.jui.uicore.image.KamelImage
|
||||
import io.kamel.image.lazyPainterResource
|
||||
import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
|
||||
expect fun Modifier.libraryMangaModifier(
|
||||
onClickManga: () -> Unit,
|
||||
@@ -102,7 +101,6 @@ private fun LibraryMangaCompactGridItem(
|
||||
showLanguage: Boolean,
|
||||
showLocal: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
@@ -113,11 +111,12 @@ private fun LibraryMangaCompactGridItem(
|
||||
.aspectRatio(mangaAspectRatio)
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
KamelImage(
|
||||
cover,
|
||||
ImageLoaderImage(
|
||||
manga,
|
||||
manga.title,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
contentScale = ContentScale.Crop,
|
||||
filterQuality = FilterQuality.Medium
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxSize() then shadowGradient)
|
||||
Text(
|
||||
|
||||
@@ -29,8 +29,7 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
|
||||
import ca.gosyer.jui.uicore.components.scrollbarPadding
|
||||
import ca.gosyer.jui.uicore.image.KamelImage
|
||||
import io.kamel.image.lazyPainterResource
|
||||
import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaCoverOnlyGrid(
|
||||
@@ -88,19 +87,18 @@ private fun LibraryMangaCoverOnlyGridItem(
|
||||
showLanguage: Boolean,
|
||||
showLocal: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
|
||||
Box(
|
||||
modifier = Modifier.padding(4.dp)
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(mangaAspectRatio)
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
KamelImage(
|
||||
cover,
|
||||
ImageLoaderImage(
|
||||
manga,
|
||||
contentDescription = manga.title,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
contentScale = ContentScale.Crop,
|
||||
filterQuality = FilterQuality.Medium
|
||||
)
|
||||
LibraryMangaBadges(
|
||||
modifier = Modifier.padding(4.dp),
|
||||
|
||||
@@ -41,14 +41,13 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
|
||||
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 com.google.accompanist.flowlayout.FlowRow
|
||||
import com.vanpra.composematerialdialogs.MaterialDialog
|
||||
import com.vanpra.composematerialdialogs.MaterialDialogState
|
||||
import com.vanpra.composematerialdialogs.listItemsMultiChoice
|
||||
import com.vanpra.composematerialdialogs.title
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun MangaItem(manga: Manga) {
|
||||
@@ -74,15 +73,16 @@ fun MangaItem(manga: Manga) {
|
||||
|
||||
@Composable
|
||||
private fun Cover(manga: Manga, modifier: Modifier = Modifier) {
|
||||
KamelImage(
|
||||
resource = lazyPainterResource(manga, filterQuality = FilterQuality.Medium),
|
||||
ImageLoaderImage(
|
||||
data = manga,
|
||||
contentDescription = manga.title,
|
||||
modifier = modifier,
|
||||
errorModifier = modifier then Modifier
|
||||
.aspectRatio(
|
||||
ratio = mangaAspectRatio,
|
||||
matchHeightConstraintsFirst = true
|
||||
)
|
||||
),
|
||||
filterQuality = FilterQuality.Medium
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -37,8 +37,7 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
|
||||
import ca.gosyer.jui.uicore.components.scrollbarPadding
|
||||
import ca.gosyer.jui.uicore.image.KamelImage
|
||||
import io.kamel.image.lazyPainterResource
|
||||
import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
|
||||
@Composable
|
||||
fun SourceMangaComfortableGrid(
|
||||
@@ -89,7 +88,6 @@ private fun SourceMangaComfortableGridItem(
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
@@ -101,14 +99,15 @@ private fun SourceMangaComfortableGridItem(
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
Column {
|
||||
KamelImage(
|
||||
cover,
|
||||
ImageLoaderImage(
|
||||
manga,
|
||||
contentDescription = manga.title,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(mangaAspectRatio)
|
||||
.clip(MaterialTheme.shapes.medium),
|
||||
contentScale = ContentScale.Crop
|
||||
contentScale = ContentScale.Crop,
|
||||
filterQuality = FilterQuality.Medium
|
||||
)
|
||||
Text(
|
||||
text = manga.title,
|
||||
|
||||
@@ -40,8 +40,7 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
|
||||
import ca.gosyer.jui.uicore.components.scrollbarPadding
|
||||
import ca.gosyer.jui.uicore.image.KamelImage
|
||||
import io.kamel.image.lazyPainterResource
|
||||
import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
|
||||
@Composable
|
||||
fun SourceMangaCompactGrid(
|
||||
@@ -92,7 +91,6 @@ private fun SourceMangaCompactGridItem(
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
@@ -103,11 +101,12 @@ private fun SourceMangaCompactGridItem(
|
||||
.aspectRatio(mangaAspectRatio)
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
KamelImage(
|
||||
cover,
|
||||
ImageLoaderImage(
|
||||
manga,
|
||||
manga.title,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
contentScale = ContentScale.Crop,
|
||||
filterQuality = FilterQuality.Medium
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxSize().then(shadowGradient))
|
||||
Text(
|
||||
|
||||
@@ -22,7 +22,6 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
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.rememberScrollbarAdapter
|
||||
import ca.gosyer.jui.uicore.components.scrollbarPadding
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun SourceMangaList(
|
||||
@@ -74,7 +72,6 @@ private fun MangaListItem(
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
MangaListItem(
|
||||
modifier = modifier then Modifier
|
||||
.requiredHeight(56.dp)
|
||||
@@ -84,7 +81,7 @@ private fun MangaListItem(
|
||||
modifier = Modifier
|
||||
.size(40.dp)
|
||||
.clip(MaterialTheme.shapes.medium),
|
||||
cover = cover,
|
||||
data = manga,
|
||||
contentDescription = manga.title
|
||||
)
|
||||
MangaListItemTitle(
|
||||
|
||||
@@ -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.rememberScrollbarAdapter
|
||||
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 io.kamel.image.lazyPainterResource
|
||||
|
||||
expect fun Modifier.sourceSideMenuItem(
|
||||
onSourceTabClick: () -> Unit,
|
||||
@@ -140,10 +139,11 @@ fun SourcesSideMenu(
|
||||
modifier = modifier
|
||||
)
|
||||
is SourceNavigatorScreen.SourceScreen -> Box(Modifier.align(Alignment.Center)) {
|
||||
KamelImage(
|
||||
resource = lazyPainterResource(screen.source, filterQuality = FilterQuality.Medium),
|
||||
ImageLoaderImage(
|
||||
data = screen.source,
|
||||
contentDescription = screen.source.displayName,
|
||||
modifier = modifier
|
||||
modifier = modifier,
|
||||
filterQuality = FilterQuality.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,8 +28,7 @@ import androidx.compose.ui.unit.times
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.ui.sources.browse.components.SourceMangaBadges
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
import ca.gosyer.jui.uicore.image.KamelImage
|
||||
import io.kamel.image.lazyPainterResource
|
||||
import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
|
||||
@Composable
|
||||
fun GlobalSearchMangaComfortableGridItem(
|
||||
@@ -37,7 +36,6 @@ fun GlobalSearchMangaComfortableGridItem(
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
@@ -49,14 +47,15 @@ fun GlobalSearchMangaComfortableGridItem(
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
Column {
|
||||
KamelImage(
|
||||
cover,
|
||||
ImageLoaderImage(
|
||||
manga,
|
||||
contentDescription = manga.title,
|
||||
modifier = Modifier
|
||||
.height(200.dp)
|
||||
.aspectRatio(mangaAspectRatio, true)
|
||||
.clip(MaterialTheme.shapes.medium),
|
||||
contentScale = ContentScale.Crop
|
||||
contentScale = ContentScale.Crop,
|
||||
filterQuality = FilterQuality.Medium
|
||||
)
|
||||
Text(
|
||||
text = manga.title,
|
||||
|
||||
@@ -31,8 +31,7 @@ import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.jui.domain.manga.model.Manga
|
||||
import ca.gosyer.jui.ui.sources.browse.components.SourceMangaBadges
|
||||
import ca.gosyer.jui.uicore.components.mangaAspectRatio
|
||||
import ca.gosyer.jui.uicore.image.KamelImage
|
||||
import io.kamel.image.lazyPainterResource
|
||||
import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
|
||||
@Composable
|
||||
fun GlobalSearchMangaCompactGridItem(
|
||||
@@ -40,7 +39,6 @@ fun GlobalSearchMangaCompactGridItem(
|
||||
manga: Manga,
|
||||
inLibrary: Boolean
|
||||
) {
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
@@ -51,11 +49,12 @@ fun GlobalSearchMangaCompactGridItem(
|
||||
.aspectRatio(mangaAspectRatio, true)
|
||||
.clip(MaterialTheme.shapes.medium) then modifier
|
||||
) {
|
||||
KamelImage(
|
||||
cover,
|
||||
ImageLoaderImage(
|
||||
manga,
|
||||
manga.title,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
contentScale = ContentScale.Crop,
|
||||
filterQuality = FilterQuality.Medium
|
||||
)
|
||||
Box(modifier = Modifier.fillMaxSize().then(shadowGradient))
|
||||
Text(
|
||||
|
||||
@@ -56,10 +56,9 @@ import ca.gosyer.jui.uicore.components.VerticalScrollbar
|
||||
import ca.gosyer.jui.uicore.components.rememberScrollbarAdapter
|
||||
import ca.gosyer.jui.uicore.components.rememberVerticalScrollbarAdapter
|
||||
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 com.vanpra.composematerialdialogs.rememberMaterialDialogState
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun SourceHomeScreenContent(
|
||||
@@ -175,7 +174,12 @@ fun WideSourceItem(
|
||||
},
|
||||
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))
|
||||
Text(
|
||||
"${source.name} (${source.displayLang.toUpperCase(Locale.current)})",
|
||||
@@ -199,11 +203,12 @@ fun ThinSourceItem(
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
KamelImage(
|
||||
lazyPainterResource(source, filterQuality = FilterQuality.Medium),
|
||||
ImageLoaderImage(
|
||||
source,
|
||||
source.displayName,
|
||||
Modifier.fillMaxHeight()
|
||||
.aspectRatio(1F, true)
|
||||
.aspectRatio(1F, true),
|
||||
filterQuality = FilterQuality.Medium
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Column {
|
||||
|
||||
@@ -26,7 +26,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
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.scrollbarPadding
|
||||
import ca.gosyer.jui.uicore.resources.stringResource
|
||||
import io.kamel.image.lazyPainterResource
|
||||
import kotlinx.datetime.LocalDate
|
||||
|
||||
@Composable
|
||||
@@ -137,7 +135,7 @@ fun UpdatesItem(
|
||||
.padding(start = 16.dp, top = 8.dp, bottom = 8.dp)
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.clickable { onClickCover() },
|
||||
cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium),
|
||||
data = manga,
|
||||
contentDescription = manga.title
|
||||
)
|
||||
MangaListItemColumn(
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
@@ -39,6 +39,7 @@ kotlin {
|
||||
api(kotlin("stdlib-common"))
|
||||
api(libs.coroutines.core)
|
||||
api(libs.kamel)
|
||||
api(libs.imageloader)
|
||||
api(libs.voyager.core)
|
||||
api(libs.dateTime)
|
||||
api(projects.core)
|
||||
|
||||
@@ -23,20 +23,19 @@ import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
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.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.jui.uicore.image.KamelImage
|
||||
import io.kamel.core.Resource
|
||||
import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
|
||||
@Composable
|
||||
fun MangaGridItem(
|
||||
title: String,
|
||||
cover: Resource<Painter>,
|
||||
data: Any,
|
||||
onClick: () -> Unit = {}
|
||||
) {
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
@@ -53,7 +52,7 @@ fun MangaGridItem(
|
||||
shape = RoundedCornerShape(4.dp)
|
||||
) {
|
||||
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))
|
||||
Text(
|
||||
text = title,
|
||||
|
||||
@@ -15,12 +15,11 @@ import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
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.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import ca.gosyer.jui.uicore.image.KamelImage
|
||||
import io.kamel.core.Resource
|
||||
import ca.gosyer.jui.uicore.image.ImageLoaderImage
|
||||
|
||||
@Composable
|
||||
fun MangaListItem(
|
||||
@@ -38,14 +37,15 @@ fun MangaListItem(
|
||||
@Composable
|
||||
fun MangaListItemImage(
|
||||
modifier: Modifier = Modifier,
|
||||
cover: Resource<Painter>,
|
||||
data: Any,
|
||||
contentDescription: String
|
||||
) {
|
||||
KamelImage(
|
||||
cover,
|
||||
ImageLoaderImage(
|
||||
data,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
contentScale = ContentScale.Crop
|
||||
contentScale = ContentScale.Crop,
|
||||
filterQuality = FilterQuality.Medium
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user