mirror of
https://github.com/Suwayomi/TachideskJUI.git
synced 2025-12-10 06:42:05 +01:00
Migrate image loading and display to Kamel
This commit is contained in:
@@ -35,6 +35,7 @@ dependencies {
|
||||
implementation("ca.gosyer:compose-router:0.24.2-jetbrains-2")
|
||||
implementation("ca.gosyer:accompanist-pager:0.18.1")
|
||||
implementation("ca.gosyer:accompanist-flowlayout:0.18.1")
|
||||
implementation("com.alialbaali.kamel:kamel-image:0.3.0")
|
||||
|
||||
// UI (Swing)
|
||||
implementation("com.github.weisj:darklaf-core:2.7.3")
|
||||
|
||||
@@ -14,6 +14,7 @@ import ca.gosyer.data.library.LibraryPreferences
|
||||
import ca.gosyer.data.reader.ReaderPreferences
|
||||
import ca.gosyer.data.server.Http
|
||||
import ca.gosyer.data.server.HttpProvider
|
||||
import ca.gosyer.data.server.KamelConfigProvider
|
||||
import ca.gosyer.data.server.ServerHostPreferences
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.ServerService
|
||||
@@ -28,6 +29,7 @@ import ca.gosyer.data.server.interactions.SourceInteractionHandler
|
||||
import ca.gosyer.data.translation.ResourceProvider
|
||||
import ca.gosyer.data.translation.XmlResourceBundle
|
||||
import ca.gosyer.data.ui.UiPreferences
|
||||
import io.kamel.core.config.KamelConfig
|
||||
import toothpick.ktp.binding.bind
|
||||
import toothpick.ktp.binding.module
|
||||
|
||||
@@ -66,6 +68,10 @@ val DataModule = module {
|
||||
.toProvider(HttpProvider::class)
|
||||
.providesSingleton()
|
||||
|
||||
bind<KamelConfig>()
|
||||
.toProvider(KamelConfigProvider::class)
|
||||
.providesSingleton()
|
||||
|
||||
bind<XmlResourceBundle>()
|
||||
.toProvider(ResourceProvider::class)
|
||||
.providesSingleton()
|
||||
|
||||
@@ -21,6 +21,4 @@ data class Extension(
|
||||
val hasUpdate: Boolean,
|
||||
val obsolete: Boolean,
|
||||
val isNsfw: Boolean
|
||||
) {
|
||||
fun iconUrl(serverUrl: String) = serverUrl + iconUrl
|
||||
}
|
||||
)
|
||||
|
||||
@@ -27,9 +27,7 @@ data class Manga(
|
||||
val meta: MangaMeta,
|
||||
val realUrl: String?,
|
||||
val inLibraryAt: Long
|
||||
) {
|
||||
fun cover(serverUrl: String) = thumbnailUrl?.let { serverUrl + it }
|
||||
}
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class MangaMeta(
|
||||
|
||||
@@ -19,7 +19,6 @@ data class Source(
|
||||
val isNsfw: Boolean,
|
||||
val displayName: String
|
||||
) {
|
||||
fun iconUrl(serverUrl: String) = serverUrl + iconUrl
|
||||
companion object {
|
||||
const val LOCAL_SOURCE_LANG = "localsourcelang"
|
||||
}
|
||||
|
||||
78
src/main/kotlin/ca/gosyer/data/server/KamelConfigProvider.kt
Normal file
78
src/main/kotlin/ca/gosyer/data/server/KamelConfigProvider.kt
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.data.server
|
||||
|
||||
import ca.gosyer.data.models.Extension
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.data.models.Source
|
||||
import ca.gosyer.ui.base.prefs.asStateIn
|
||||
import io.kamel.core.config.DefaultCacheSize
|
||||
import io.kamel.core.config.KamelConfig
|
||||
import io.kamel.core.config.fileFetcher
|
||||
import io.kamel.core.config.httpFetcher
|
||||
import io.kamel.core.config.stringMapper
|
||||
import io.kamel.core.config.uriMapper
|
||||
import io.kamel.core.config.urlMapper
|
||||
import io.kamel.core.mapper.Mapper
|
||||
import io.kamel.image.config.imageBitmapDecoder
|
||||
import io.kamel.image.config.resourcesFetcher
|
||||
import io.ktor.http.Url
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Provider
|
||||
|
||||
class KamelConfigProvider @Inject constructor(
|
||||
private val http: Http,
|
||||
serverPreferences: ServerPreferences
|
||||
) : Provider<KamelConfig> {
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
val serverUrl = serverPreferences.serverUrl().asStateIn(GlobalScope)
|
||||
|
||||
override fun get(): KamelConfig {
|
||||
return KamelConfig {
|
||||
// Default config
|
||||
imageBitmapCacheSize = DefaultCacheSize
|
||||
imageVectorCacheSize = DefaultCacheSize
|
||||
imageBitmapDecoder()
|
||||
stringMapper()
|
||||
urlMapper()
|
||||
uriMapper()
|
||||
fileFetcher()
|
||||
|
||||
// JUI config
|
||||
httpFetcher(http.engine) {
|
||||
install(http)
|
||||
}
|
||||
resourcesFetcher()
|
||||
val serverUrl = serverUrl.asStateFlow()
|
||||
mapper(MangaCoverMapper(serverUrl))
|
||||
mapper(ExtensionIconMapper(serverUrl))
|
||||
mapper(SourceIconMapper(serverUrl))
|
||||
}
|
||||
}
|
||||
|
||||
class MangaCoverMapper(private val serverUrlStateFlow: StateFlow<String>) : Mapper<Manga, Url> {
|
||||
override fun map(input: Manga): Url {
|
||||
return Url(serverUrlStateFlow.value + input.thumbnailUrl)
|
||||
}
|
||||
}
|
||||
|
||||
class ExtensionIconMapper(private val serverUrlStateFlow: StateFlow<String>) : Mapper<Extension, Url> {
|
||||
override fun map(input: Extension): Url {
|
||||
return Url(serverUrlStateFlow.value + input.iconUrl)
|
||||
}
|
||||
}
|
||||
|
||||
class SourceIconMapper(private val serverUrlStateFlow: StateFlow<String>) : Mapper<Source, Url> {
|
||||
override fun map(input: Source): Url {
|
||||
return Url(serverUrlStateFlow.value + input.iconUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,8 @@ import ca.gosyer.ui.base.components.setIcon
|
||||
import ca.gosyer.ui.base.resources.LocalResources
|
||||
import ca.gosyer.ui.base.theme.AppTheme
|
||||
import ca.gosyer.util.lang.launchApplication
|
||||
import io.kamel.core.config.KamelConfig
|
||||
import io.kamel.image.config.LocalKamelConfig
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@@ -69,6 +71,7 @@ fun WindowDialog(
|
||||
}
|
||||
|
||||
val resources = remember { AppScope.getInstance<XmlResourceBundle>() }
|
||||
val kamelConfig = remember { AppScope.getInstance<KamelConfig>() }
|
||||
val windowState = rememberWindowState(size = size, position = WindowPosition(Alignment.Center))
|
||||
|
||||
Window(
|
||||
@@ -95,7 +98,8 @@ fun WindowDialog(
|
||||
) {
|
||||
setIcon()
|
||||
CompositionLocalProvider(
|
||||
LocalResources provides resources
|
||||
LocalResources provides resources,
|
||||
LocalKamelConfig provides kamelConfig
|
||||
) {
|
||||
AppTheme {
|
||||
Surface {
|
||||
@@ -144,6 +148,7 @@ fun WindowDialog(
|
||||
}
|
||||
|
||||
val resources = remember { AppScope.getInstance<XmlResourceBundle>() }
|
||||
val kamelConfig = remember { AppScope.getInstance<KamelConfig>() }
|
||||
val windowState = rememberWindowState(size = size, position = WindowPosition.Aligned(Alignment.Center))
|
||||
|
||||
Window(
|
||||
@@ -162,7 +167,8 @@ fun WindowDialog(
|
||||
) {
|
||||
setIcon()
|
||||
CompositionLocalProvider(
|
||||
LocalResources provides resources
|
||||
LocalResources provides resources,
|
||||
LocalKamelConfig provides kamelConfig
|
||||
) {
|
||||
AppTheme {
|
||||
Surface {
|
||||
|
||||
53
src/main/kotlin/ca/gosyer/ui/base/components/KamelImage.kt
Normal file
53
src/main/kotlin/ca/gosyer/ui/base/components/KamelImage.kt
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* 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.ui.base.components
|
||||
|
||||
import androidx.compose.animation.core.FiniteAnimationSpec
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.DefaultAlpha
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import io.kamel.core.Resource
|
||||
import io.kamel.image.KamelImage as BaseKamelImage
|
||||
|
||||
@Composable
|
||||
fun KamelImage(
|
||||
resource: Resource<Painter>,
|
||||
contentDescription: String?,
|
||||
modifier: Modifier = Modifier,
|
||||
alignment: Alignment = Alignment.Center,
|
||||
contentScale: ContentScale = ContentScale.Fit,
|
||||
alpha: Float = DefaultAlpha,
|
||||
colorFilter: ColorFilter? = null,
|
||||
onLoading: @Composable (Float) -> Unit = {
|
||||
LoadingScreen(progress = it, modifier = modifier then Modifier.fillMaxSize())
|
||||
},
|
||||
onFailure: @Composable (Throwable) -> Unit = {
|
||||
ErrorScreen(it.localizedMessage, modifier = modifier then Modifier.fillMaxSize())
|
||||
},
|
||||
crossfade: Boolean = true,
|
||||
animationSpec: FiniteAnimationSpec<Float> = tween()
|
||||
) {
|
||||
BaseKamelImage(
|
||||
resource,
|
||||
contentDescription,
|
||||
modifier,
|
||||
alignment,
|
||||
contentScale,
|
||||
alpha,
|
||||
colorFilter,
|
||||
onLoading,
|
||||
onFailure,
|
||||
crossfade,
|
||||
animationSpec
|
||||
)
|
||||
}
|
||||
@@ -1,100 +0,0 @@
|
||||
/*
|
||||
* 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.ui.base.components
|
||||
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
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.ColorFilter
|
||||
import androidx.compose.ui.graphics.DefaultAlpha
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import ca.gosyer.common.di.AppScope
|
||||
import ca.gosyer.data.server.Http
|
||||
import ca.gosyer.util.compose.imageFromUrl
|
||||
import ca.gosyer.util.system.kLogger
|
||||
import io.ktor.client.features.onDownload
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Semaphore
|
||||
import kotlinx.coroutines.sync.withPermit
|
||||
|
||||
private val logger = kLogger {}
|
||||
private val semaphore = Semaphore(5)
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@Composable
|
||||
fun KtorImage(
|
||||
imageUrl: String,
|
||||
modifier: Modifier = Modifier.fillMaxSize(),
|
||||
loadingModifier: Modifier = modifier,
|
||||
contentDescription: String? = null,
|
||||
alignment: Alignment = Alignment.Center,
|
||||
contentScale: ContentScale = ContentScale.Fit,
|
||||
alpha: Float = DefaultAlpha,
|
||||
colorFilter: ColorFilter? = null,
|
||||
filterQuality: FilterQuality = FilterQuality.Medium,
|
||||
client: Http = remember { AppScope.getInstance() }
|
||||
) {
|
||||
BoxWithConstraints(modifier) {
|
||||
val drawable = remember { mutableStateOf<ImageBitmap?>(null) }
|
||||
val loading = remember { mutableStateOf(true) }
|
||||
val progress = remember { mutableStateOf(0.0F) }
|
||||
val error = remember { mutableStateOf<String?>(null) }
|
||||
DisposableEffect(imageUrl) {
|
||||
val handler = CoroutineExceptionHandler { _, throwable ->
|
||||
logger.error(throwable) { "Error loading image $imageUrl" }
|
||||
loading.value = false
|
||||
error.value = throwable.message
|
||||
}
|
||||
val job = GlobalScope.launch(handler) {
|
||||
if (drawable.value == null) {
|
||||
semaphore.withPermit {
|
||||
drawable.value = imageFromUrl(client, imageUrl) {
|
||||
onDownload { bytesSentTotal, contentLength ->
|
||||
progress.value = (bytesSentTotal.toFloat() / contentLength).coerceAtMost(1.0F)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
|
||||
onDispose {
|
||||
job.cancel()
|
||||
drawable.value = null
|
||||
}
|
||||
}
|
||||
|
||||
Crossfade(drawable.value to loading.value) { (value, loading) ->
|
||||
if (value != null) {
|
||||
Image(
|
||||
value,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentDescription = contentDescription,
|
||||
alignment = alignment,
|
||||
contentScale = contentScale,
|
||||
alpha = alpha,
|
||||
colorFilter = colorFilter,
|
||||
filterQuality = filterQuality
|
||||
)
|
||||
} else {
|
||||
LoadingScreen(loading, loadingModifier, progress.value, error.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,17 +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.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 io.kamel.core.Resource
|
||||
|
||||
@Composable
|
||||
fun MangaGridItem(
|
||||
title: String,
|
||||
cover: String?,
|
||||
cover: Resource<Painter>,
|
||||
onClick: () -> Unit = {},
|
||||
) {
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
@@ -50,9 +52,7 @@ fun MangaGridItem(
|
||||
shape = RoundedCornerShape(4.dp)
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
if (cover != null) {
|
||||
KtorImage(cover, contentScale = ContentScale.Crop)
|
||||
}
|
||||
KamelImage(cover, title, contentScale = ContentScale.Crop)
|
||||
Box(modifier = Modifier.fillMaxSize().then(shadowGradient))
|
||||
Text(
|
||||
text = title,
|
||||
|
||||
@@ -15,9 +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.layout.ContentScale
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import io.kamel.core.Resource
|
||||
|
||||
@Composable
|
||||
fun MangaListItem(
|
||||
@@ -35,18 +37,15 @@ fun MangaListItem(
|
||||
@Composable
|
||||
fun MangaListItemImage(
|
||||
modifier: Modifier = Modifier,
|
||||
imageUrl: String?
|
||||
cover: Resource<Painter>,
|
||||
contentDescription: String
|
||||
) {
|
||||
if (imageUrl != null) {
|
||||
KtorImage(
|
||||
imageUrl = imageUrl,
|
||||
contentDescription = null,
|
||||
modifier = modifier,
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
} else {
|
||||
ErrorScreen(modifier = modifier)
|
||||
}
|
||||
KamelImage(
|
||||
cover,
|
||||
contentDescription = contentDescription,
|
||||
modifier = modifier,
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -39,6 +39,7 @@ 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.FilterQuality
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.withStyle
|
||||
@@ -50,7 +51,7 @@ import ca.gosyer.build.BuildConfig
|
||||
import ca.gosyer.data.models.Extension
|
||||
import ca.gosyer.ui.base.WindowDialog
|
||||
import ca.gosyer.ui.base.components.ActionIcon
|
||||
import ca.gosyer.ui.base.components.KtorImage
|
||||
import ca.gosyer.ui.base.components.KamelImage
|
||||
import ca.gosyer.ui.base.components.LoadingScreen
|
||||
import ca.gosyer.ui.base.components.Toolbar
|
||||
import ca.gosyer.ui.base.resources.stringResource
|
||||
@@ -58,6 +59,7 @@ import ca.gosyer.ui.base.vm.viewModel
|
||||
import ca.gosyer.util.compose.ThemedWindow
|
||||
import ca.gosyer.util.compose.persistentLazyListState
|
||||
import ca.gosyer.util.lang.launchApplication
|
||||
import io.kamel.image.lazyPainterResource
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -80,7 +82,6 @@ fun ExtensionsMenu() {
|
||||
val vm = viewModel<ExtensionsMenuViewModel>()
|
||||
val extensions by vm.extensions.collectAsState()
|
||||
val isLoading by vm.isLoading.collectAsState()
|
||||
val serverUrl by vm.serverUrl.collectAsState()
|
||||
val search by vm.searchQuery.collectAsState()
|
||||
|
||||
if (isLoading) {
|
||||
@@ -119,7 +120,6 @@ fun ExtensionsMenu() {
|
||||
items(items) { extension ->
|
||||
ExtensionItem(
|
||||
extension,
|
||||
serverUrl,
|
||||
onInstallClicked = vm::install,
|
||||
onUpdateClicked = vm::update,
|
||||
onUninstallClicked = vm::uninstall
|
||||
@@ -167,7 +167,6 @@ fun ExtensionsToolbar(
|
||||
@Composable
|
||||
fun ExtensionItem(
|
||||
extension: Extension,
|
||||
serverUrl: String,
|
||||
onInstallClicked: (Extension) -> Unit,
|
||||
onUpdateClicked: (Extension) -> Unit,
|
||||
onUninstallClicked: (Extension) -> Unit
|
||||
@@ -175,7 +174,7 @@ 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))
|
||||
KtorImage(extension.iconUrl(serverUrl), Modifier.size(50.dp))
|
||||
KamelImage(lazyPainterResource(extension, filterQuality = FilterQuality.Medium), extension.name, Modifier.size(50.dp))
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Column {
|
||||
val title = buildAnnotatedString {
|
||||
|
||||
@@ -8,7 +8,6 @@ package ca.gosyer.ui.extensions
|
||||
|
||||
import ca.gosyer.data.extension.ExtensionPreferences
|
||||
import ca.gosyer.data.models.Extension
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.interactions.ExtensionInteractionHandler
|
||||
import ca.gosyer.data.translation.XmlResourceBundle
|
||||
import ca.gosyer.ui.base.vm.ViewModel
|
||||
@@ -26,10 +25,8 @@ import javax.inject.Inject
|
||||
class ExtensionsMenuViewModel @Inject constructor(
|
||||
private val extensionHandler: ExtensionInteractionHandler,
|
||||
private val resources: XmlResourceBundle,
|
||||
serverPreferences: ServerPreferences,
|
||||
extensionPreferences: ExtensionPreferences
|
||||
) : ViewModel() {
|
||||
val serverUrl = serverPreferences.serverUrl().stateIn(scope)
|
||||
private val _enabledLangs = extensionPreferences.languages().asStateFlow()
|
||||
val enabledLangs = _enabledLangs.asStateFlow()
|
||||
|
||||
|
||||
@@ -73,7 +73,6 @@ fun LibraryScreen(bundle: Bundle, onClickManga: (Long) -> Unit = { openMangaMenu
|
||||
val displayMode by vm.displayMode.collectAsState()
|
||||
val isLoading by vm.isLoading.collectAsState()
|
||||
val error by vm.error.collectAsState()
|
||||
val serverUrl by vm.serverUrl.collectAsState()
|
||||
val query by vm.query.collectAsState()
|
||||
// val sheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
|
||||
|
||||
@@ -116,7 +115,6 @@ fun LibraryScreen(bundle: Bundle, onClickManga: (Long) -> Unit = { openMangaMenu
|
||||
categories = categories,
|
||||
displayMode = displayMode,
|
||||
selectedPage = selectedCategoryIndex,
|
||||
serverUrl = serverUrl,
|
||||
getLibraryForPage = { vm.getLibraryForCategoryIndex(it).collectAsState() },
|
||||
onPageChanged = vm::setSelectedPage,
|
||||
onClickManga = onClickManga,
|
||||
@@ -163,7 +161,6 @@ private fun LibraryPager(
|
||||
categories: List<Category>,
|
||||
displayMode: DisplayMode,
|
||||
selectedPage: Int,
|
||||
serverUrl: String,
|
||||
getLibraryForPage: @Composable (Int) -> State<List<Manga>>,
|
||||
onPageChanged: (Int) -> Unit,
|
||||
onClickManga: (Long) -> Unit,
|
||||
@@ -187,7 +184,6 @@ private fun LibraryPager(
|
||||
when (displayMode) {
|
||||
DisplayMode.CompactGrid -> LibraryMangaCompactGrid(
|
||||
library = library,
|
||||
serverUrl = serverUrl,
|
||||
onClickManga = onClickManga,
|
||||
onRemoveMangaClicked = onRemoveMangaClicked
|
||||
)
|
||||
|
||||
@@ -9,7 +9,6 @@ package ca.gosyer.ui.library
|
||||
import ca.gosyer.data.library.LibraryPreferences
|
||||
import ca.gosyer.data.models.Category
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.interactions.CategoryInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.LibraryInteractionHandler
|
||||
import ca.gosyer.ui.base.vm.ViewModel
|
||||
@@ -73,11 +72,8 @@ class LibraryScreenViewModel @Inject constructor(
|
||||
private val bundle: Bundle,
|
||||
private val categoryHandler: CategoryInteractionHandler,
|
||||
private val libraryHandler: LibraryInteractionHandler,
|
||||
libraryPreferences: LibraryPreferences,
|
||||
serverPreferences: ServerPreferences,
|
||||
libraryPreferences: LibraryPreferences
|
||||
) : ViewModel() {
|
||||
val serverUrl = serverPreferences.serverUrl().stateIn(scope)
|
||||
|
||||
private val library = Library(MutableStateFlow(emptyList()), mutableMapOf())
|
||||
val categories = library.categories.asStateFlow()
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@ import androidx.compose.material.LocalTextStyle
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -27,19 +26,20 @@ 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.FilterQuality
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.ui.base.components.KtorImage
|
||||
import ca.gosyer.ui.base.components.KamelImage
|
||||
import ca.gosyer.ui.base.components.contextMenuClickable
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun LibraryMangaCompactGrid(
|
||||
library: List<Manga>,
|
||||
serverUrl: String,
|
||||
onClickManga: (Long) -> Unit = {},
|
||||
onRemoveMangaClicked: (Long) -> Unit = {}
|
||||
) {
|
||||
@@ -52,7 +52,6 @@ fun LibraryMangaCompactGrid(
|
||||
manga = manga,
|
||||
unread = null, // TODO
|
||||
downloaded = null, // TODO
|
||||
serverUrl = serverUrl,
|
||||
onClick = { onClickManga(manga.id) }
|
||||
) {
|
||||
listOf(
|
||||
@@ -68,11 +67,10 @@ private fun LibraryMangaCompactGridItem(
|
||||
manga: Manga,
|
||||
unread: Int?,
|
||||
downloaded: Int?,
|
||||
serverUrl: String,
|
||||
onClick: () -> Unit = {},
|
||||
contextMenuItems: () -> List<ContextMenuItem> = { emptyList() }
|
||||
) {
|
||||
val cover = remember(manga.id, serverUrl) { manga.cover(serverUrl) }
|
||||
val cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium)
|
||||
val fontStyle = LocalTextStyle.current.merge(
|
||||
TextStyle(letterSpacing = 0.sp, fontFamily = FontFamily.SansSerif, fontSize = 14.sp)
|
||||
)
|
||||
@@ -87,9 +85,7 @@ private fun LibraryMangaCompactGridItem(
|
||||
items = contextMenuItems
|
||||
)
|
||||
) {
|
||||
if (cover != null) {
|
||||
KtorImage(cover, contentScale = ContentScale.Crop)
|
||||
}
|
||||
KamelImage(cover, manga.title, contentScale = ContentScale.Crop)
|
||||
Box(modifier = Modifier.fillMaxSize().then(shadowGradient))
|
||||
Text(
|
||||
text = manga.title,
|
||||
|
||||
@@ -48,6 +48,8 @@ import com.github.weisj.darklaf.theme.IntelliJTheme
|
||||
import com.github.zsoltk.compose.backpress.BackPressHandler
|
||||
import com.github.zsoltk.compose.backpress.LocalBackPressHandler
|
||||
import com.github.zsoltk.compose.savedinstancestate.Bundle
|
||||
import io.kamel.core.config.KamelConfig
|
||||
import io.kamel.image.config.LocalKamelConfig
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -94,6 +96,7 @@ suspend fun main() {
|
||||
}
|
||||
|
||||
val resources = scope.getInstance<XmlResourceBundle>()
|
||||
val kamelConfig = scope.getInstance<KamelConfig>()
|
||||
|
||||
// Set the Compose constants before any
|
||||
// Swing functions are called
|
||||
@@ -181,7 +184,8 @@ suspend fun main() {
|
||||
CompositionLocalProvider(
|
||||
LocalComposeWindow provides window,
|
||||
LocalBackPressHandler provides backPressHandler,
|
||||
LocalResources provides resources
|
||||
LocalResources provides resources,
|
||||
LocalKamelConfig provides kamelConfig
|
||||
) {
|
||||
Crossfade(serverService.initialized.collectAsState().value) { initialized ->
|
||||
when (initialized) {
|
||||
|
||||
@@ -40,6 +40,7 @@ import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
@@ -50,7 +51,7 @@ import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.ui.base.WindowDialog
|
||||
import ca.gosyer.ui.base.components.ActionIcon
|
||||
import ca.gosyer.ui.base.components.ErrorScreen
|
||||
import ca.gosyer.ui.base.components.KtorImage
|
||||
import ca.gosyer.ui.base.components.KamelImage
|
||||
import ca.gosyer.ui.base.components.LoadingScreen
|
||||
import ca.gosyer.ui.base.components.LocalMenuController
|
||||
import ca.gosyer.ui.base.components.MenuController
|
||||
@@ -61,6 +62,7 @@ import ca.gosyer.ui.reader.openReaderMenu
|
||||
import ca.gosyer.util.compose.ThemedWindow
|
||||
import ca.gosyer.util.lang.launchApplication
|
||||
import com.google.accompanist.flowlayout.FlowRow
|
||||
import io.kamel.image.lazyPainterResource
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.collect
|
||||
@@ -84,7 +86,6 @@ fun MangaMenu(mangaId: Long, menuController: MenuController? = LocalMenuControll
|
||||
val manga by vm.manga.collectAsState()
|
||||
val chapters by vm.chapters.collectAsState()
|
||||
val isLoading by vm.isLoading.collectAsState()
|
||||
val serverUrl by vm.serverUrl.collectAsState()
|
||||
val dateTimeFormatter by vm.dateTimeFormatter.collectAsState()
|
||||
val categoriesExist by vm.categoriesExist.collectAsState()
|
||||
|
||||
@@ -128,7 +129,7 @@ fun MangaMenu(mangaId: Long, menuController: MenuController? = LocalMenuControll
|
||||
val state = rememberLazyListState()
|
||||
LazyColumn(state = state) {
|
||||
item {
|
||||
MangaItem(manga, serverUrl)
|
||||
MangaItem(manga)
|
||||
}
|
||||
if (chapters.isNotEmpty()) {
|
||||
items(chapters) { chapter ->
|
||||
@@ -171,17 +172,17 @@ fun MangaMenu(mangaId: Long, menuController: MenuController? = LocalMenuControll
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MangaItem(manga: Manga, serverUrl: String) {
|
||||
fun MangaItem(manga: Manga) {
|
||||
BoxWithConstraints(Modifier.padding(8.dp)) {
|
||||
if (maxWidth > 600.dp) {
|
||||
Row {
|
||||
Cover(manga, serverUrl, Modifier.width(300.dp))
|
||||
Cover(manga, Modifier.width(300.dp))
|
||||
Spacer(Modifier.width(16.dp))
|
||||
MangaInfo(manga)
|
||||
}
|
||||
} else {
|
||||
Column {
|
||||
Cover(manga, serverUrl, Modifier.align(Alignment.CenterHorizontally))
|
||||
Cover(manga, Modifier.align(Alignment.CenterHorizontally))
|
||||
Spacer(Modifier.height(16.dp))
|
||||
MangaInfo(manga)
|
||||
}
|
||||
@@ -190,17 +191,17 @@ fun MangaItem(manga: Manga, serverUrl: String) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Cover(manga: Manga, serverUrl: String, modifier: Modifier = Modifier) {
|
||||
private fun Cover(manga: Manga, modifier: Modifier = Modifier) {
|
||||
Surface(
|
||||
modifier = modifier then Modifier
|
||||
.padding(4.dp),
|
||||
shape = RoundedCornerShape(4.dp)
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
manga.cover(serverUrl)?.let {
|
||||
KtorImage(it)
|
||||
}
|
||||
}
|
||||
KamelImage(
|
||||
lazyPainterResource(manga, filterQuality = FilterQuality.Medium),
|
||||
manga.title,
|
||||
Modifier.fillMaxSize()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ import ca.gosyer.data.download.DownloadService
|
||||
import ca.gosyer.data.models.Category
|
||||
import ca.gosyer.data.models.Chapter
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.interactions.CategoryInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.ChapterInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.LibraryInteractionHandler
|
||||
@@ -41,11 +40,8 @@ class MangaMenuViewModel @Inject constructor(
|
||||
private val categoryHandler: CategoryInteractionHandler,
|
||||
private val libraryHandler: LibraryInteractionHandler,
|
||||
private val downloadService: DownloadService,
|
||||
serverPreferences: ServerPreferences,
|
||||
uiPreferences: UiPreferences,
|
||||
) : ViewModel() {
|
||||
val serverUrl = serverPreferences.serverUrl().stateIn(scope)
|
||||
|
||||
private val downloadingChapters = downloadService.registerWatch(params.mangaId)
|
||||
|
||||
private val _manga = MutableStateFlow<Manga?>(null)
|
||||
|
||||
@@ -66,6 +66,8 @@ import ca.gosyer.ui.reader.navigation.navigationClickable
|
||||
import ca.gosyer.ui.reader.viewer.ContinuousReader
|
||||
import ca.gosyer.ui.reader.viewer.PagerReader
|
||||
import ca.gosyer.util.lang.launchApplication
|
||||
import io.kamel.core.config.KamelConfig
|
||||
import io.kamel.image.config.LocalKamelConfig
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
|
||||
@OptIn(DelicateCoroutinesApi::class)
|
||||
@@ -79,6 +81,7 @@ fun openReaderMenu(chapterIndex: Int, mangaId: Long) {
|
||||
) = windowSettings.get().get()
|
||||
|
||||
val resources = AppScope.getInstance<XmlResourceBundle>()
|
||||
val kamelConfig = AppScope.getInstance<KamelConfig>()
|
||||
|
||||
launchApplication {
|
||||
var shortcuts by remember {
|
||||
@@ -110,7 +113,8 @@ fun openReaderMenu(chapterIndex: Int, mangaId: Long) {
|
||||
setIcon()
|
||||
CompositionLocalProvider(
|
||||
LocalComposeWindow provides window,
|
||||
LocalResources provides resources
|
||||
LocalResources provides resources,
|
||||
LocalKamelConfig provides kamelConfig
|
||||
) {
|
||||
AppTheme {
|
||||
ReaderMenu(chapterIndex, mangaId) { shortcuts = it }
|
||||
|
||||
@@ -35,11 +35,12 @@ import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.unit.DpOffset
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.build.BuildConfig
|
||||
import ca.gosyer.ui.base.components.ActionIcon
|
||||
import ca.gosyer.ui.base.components.KtorImage
|
||||
import ca.gosyer.ui.base.components.KamelImage
|
||||
import ca.gosyer.ui.base.components.Toolbar
|
||||
import ca.gosyer.ui.base.components.combinedMouseClickable
|
||||
import ca.gosyer.ui.base.resources.stringResource
|
||||
@@ -54,6 +55,7 @@ import ca.gosyer.util.lang.launchApplication
|
||||
import com.github.zsoltk.compose.savedinstancestate.Bundle
|
||||
import com.github.zsoltk.compose.savedinstancestate.BundleScope
|
||||
import com.github.zsoltk.compose.savedinstancestate.LocalSavedInstanceState
|
||||
import io.kamel.image.lazyPainterResource
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
@@ -91,7 +93,6 @@ fun SourcesMenu(bundle: Bundle, onSourceSettingsClick: (Long) -> Unit, onMangaCl
|
||||
val selectedSourceTab by vm.selectedSourceTab.collectAsState()
|
||||
val sourceSearchEnabled by vm.sourceSearchEnabled.collectAsState()
|
||||
val sourceSearchQuery by vm.sourceSearchQuery.collectAsState()
|
||||
val serverUrl by vm.serverUrl.collectAsState()
|
||||
Column {
|
||||
Toolbar(
|
||||
selectedSourceTab?.name ?: stringResource("location_sources"),
|
||||
@@ -165,7 +166,13 @@ fun SourcesMenu(bundle: Bundle, onSourceSettingsClick: (Long) -> Unit, onMangaCl
|
||||
.requiredSize(50.dp)
|
||||
.align(Alignment.Center)
|
||||
if (source != null) {
|
||||
KtorImage(source.iconUrl(serverUrl), modifier = modifier)
|
||||
Box(Modifier.align(Alignment.Center)) {
|
||||
KamelImage(
|
||||
lazyPainterResource(source, filterQuality = FilterQuality.Medium),
|
||||
source.displayName,
|
||||
modifier
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Icon(Icons.Rounded.Home, stringResource("sources_home"), modifier = modifier)
|
||||
}
|
||||
@@ -180,7 +187,7 @@ fun SourcesMenu(bundle: Bundle, onSourceSettingsClick: (Long) -> Unit, onMangaCl
|
||||
if (selectedSource != null) {
|
||||
SourceScreen(it, selectedSource, onMangaClick, vm::enableSearch, vm::setSearch)
|
||||
} else {
|
||||
SourceHomeScreen(isLoading, sources, serverUrl, vm::addTab)
|
||||
SourceHomeScreen(isLoading, sources, vm::addTab)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ package ca.gosyer.ui.sources
|
||||
|
||||
import ca.gosyer.data.catalog.CatalogPreferences
|
||||
import ca.gosyer.data.models.Source
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.interactions.SourceInteractionHandler
|
||||
import ca.gosyer.ui.base.vm.ViewModel
|
||||
import ca.gosyer.util.lang.throwIfCancellation
|
||||
@@ -25,11 +24,8 @@ import javax.inject.Inject
|
||||
class SourcesMenuViewModel @Inject constructor(
|
||||
private val bundle: Bundle,
|
||||
private val sourceHandler: SourceInteractionHandler,
|
||||
serverPreferences: ServerPreferences,
|
||||
catalogPreferences: CatalogPreferences
|
||||
) : ViewModel() {
|
||||
val serverUrl = serverPreferences.serverUrl().stateIn(scope)
|
||||
|
||||
private val _languages = catalogPreferences.languages().asStateFlow()
|
||||
val languages = _languages.asStateFlow()
|
||||
|
||||
|
||||
@@ -33,17 +33,18 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.data.models.Source
|
||||
import ca.gosyer.ui.base.components.KtorImage
|
||||
import ca.gosyer.ui.base.components.KamelImage
|
||||
import ca.gosyer.ui.base.components.LoadingScreen
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun SourceHomeScreen(
|
||||
isLoading: Boolean,
|
||||
sources: List<Source>,
|
||||
serverUrl: String,
|
||||
onSourceClicked: (Source) -> Unit
|
||||
) {
|
||||
if (sources.isEmpty()) {
|
||||
@@ -51,7 +52,7 @@ fun SourceHomeScreen(
|
||||
} else {
|
||||
Box(Modifier.fillMaxSize(), Alignment.TopCenter) {
|
||||
val state = rememberLazyListState()
|
||||
SourceCategory(sources, serverUrl, onSourceClicked, state)
|
||||
SourceCategory(sources, onSourceClicked, state)
|
||||
/*val sourcesByLang = sources.groupBy { it.lang.toLowerCase() }.toList()
|
||||
LazyColumn(state = state) {
|
||||
items(sourcesByLang) { (lang, sources) ->
|
||||
@@ -75,7 +76,6 @@ fun SourceHomeScreen(
|
||||
@Composable
|
||||
fun SourceCategory(
|
||||
sources: List<Source>,
|
||||
serverUrl: String,
|
||||
onSourceClicked: (Source) -> Unit,
|
||||
state: LazyListState
|
||||
) {
|
||||
@@ -83,7 +83,6 @@ fun SourceCategory(
|
||||
items(sources) { source ->
|
||||
SourceItem(
|
||||
source,
|
||||
serverUrl,
|
||||
onSourceClicked = onSourceClicked
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
@@ -94,7 +93,6 @@ fun SourceCategory(
|
||||
@Composable
|
||||
fun SourceItem(
|
||||
source: Source,
|
||||
serverUrl: String,
|
||||
onSourceClicked: (Source) -> Unit
|
||||
) {
|
||||
TooltipArea(
|
||||
@@ -117,7 +115,7 @@ fun SourceItem(
|
||||
},
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
KtorImage(source.iconUrl(serverUrl), Modifier.size(96.dp))
|
||||
KamelImage(lazyPainterResource(source, filterQuality = FilterQuality.Medium), source.displayName, Modifier.size(96.dp))
|
||||
Spacer(Modifier.height(4.dp))
|
||||
Text(
|
||||
"${source.name} (${source.lang.uppercase()})",
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.FilterQuality
|
||||
import androidx.compose.ui.unit.dp
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.data.models.Source
|
||||
@@ -30,6 +31,7 @@ import ca.gosyer.ui.base.resources.stringResource
|
||||
import ca.gosyer.ui.base.vm.viewModel
|
||||
import ca.gosyer.util.compose.persistentLazyListState
|
||||
import com.github.zsoltk.compose.savedinstancestate.Bundle
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun SourceScreen(
|
||||
@@ -46,7 +48,6 @@ fun SourceScreen(
|
||||
val hasNextPage by vm.hasNextPage.collectAsState()
|
||||
val loading by vm.loading.collectAsState()
|
||||
val isLatest by vm.isLatest.collectAsState()
|
||||
val serverUrl by vm.serverUrl.collectAsState()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
setSearch(vm::search)
|
||||
@@ -67,7 +68,6 @@ fun SourceScreen(
|
||||
hasNextPage,
|
||||
source.supportsLatest,
|
||||
isLatest,
|
||||
serverUrl,
|
||||
onLoadNextPage = vm::loadNextPage,
|
||||
onMangaClick = onMangaClick,
|
||||
onClickMode = vm::setMode
|
||||
@@ -82,7 +82,6 @@ private fun MangaTable(
|
||||
hasNextPage: Boolean = false,
|
||||
supportsLatest: Boolean,
|
||||
isLatest: Boolean,
|
||||
serverUrl: String,
|
||||
onLoadNextPage: () -> Unit,
|
||||
onMangaClick: (Long) -> Unit,
|
||||
onClickMode: (Boolean) -> Unit
|
||||
@@ -111,7 +110,7 @@ private fun MangaTable(
|
||||
}
|
||||
MangaGridItem(
|
||||
title = manga.title,
|
||||
cover = manga.cover(serverUrl),
|
||||
cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium),
|
||||
onClick = {
|
||||
onMangaClick(manga.id)
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ package ca.gosyer.ui.sources.components
|
||||
import ca.gosyer.data.models.Manga
|
||||
import ca.gosyer.data.models.MangaPage
|
||||
import ca.gosyer.data.models.Source
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.interactions.SourceInteractionHandler
|
||||
import ca.gosyer.ui.base.vm.ViewModel
|
||||
import ca.gosyer.util.compose.saveBooleanInBundle
|
||||
@@ -26,21 +25,17 @@ import javax.inject.Inject
|
||||
class SourceScreenViewModel(
|
||||
private val source: Source,
|
||||
private val bundle: Bundle,
|
||||
private val sourceHandler: SourceInteractionHandler,
|
||||
serverPreferences: ServerPreferences
|
||||
private val sourceHandler: SourceInteractionHandler
|
||||
) : ViewModel() {
|
||||
|
||||
@Inject constructor(
|
||||
params: Params,
|
||||
sourceHandler: SourceInteractionHandler,
|
||||
serverPreferences: ServerPreferences
|
||||
sourceHandler: SourceInteractionHandler
|
||||
) : this(
|
||||
params.source,
|
||||
params.bundle,
|
||||
sourceHandler,
|
||||
serverPreferences
|
||||
sourceHandler
|
||||
)
|
||||
val serverUrl = serverPreferences.serverUrl().stateIn(scope)
|
||||
|
||||
private val _mangas = saveObjectInBundle(scope, bundle, MANGAS_KEY) { emptyList<Manga>() }
|
||||
val mangas = _mangas.asStateFlow()
|
||||
|
||||
@@ -22,6 +22,7 @@ import androidx.compose.runtime.getValue
|
||||
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.data.models.Chapter
|
||||
@@ -37,6 +38,7 @@ import ca.gosyer.ui.base.components.Toolbar
|
||||
import ca.gosyer.ui.base.components.mangaAspectRatio
|
||||
import ca.gosyer.ui.base.resources.stringResource
|
||||
import ca.gosyer.ui.base.vm.viewModel
|
||||
import io.kamel.image.lazyPainterResource
|
||||
|
||||
@Composable
|
||||
fun UpdatesMenu(
|
||||
@@ -44,7 +46,6 @@ fun UpdatesMenu(
|
||||
openManga: (Long) -> Unit
|
||||
) {
|
||||
val vm = viewModel<UpdatesMenuViewModel>()
|
||||
val serverUrl by vm.serverUrl.collectAsState()
|
||||
val isLoading by vm.isLoading.collectAsState()
|
||||
val updates by vm.updates.collectAsState()
|
||||
Column {
|
||||
@@ -57,7 +58,6 @@ fun UpdatesMenu(
|
||||
val manga = it.manga!!
|
||||
val chapter = it.chapter
|
||||
UpdatesItem(
|
||||
serverUrl,
|
||||
it,
|
||||
onClickItem = { openChapter(chapter.index, chapter.mangaId) },
|
||||
onClickCover = { openManga(manga.id) },
|
||||
@@ -73,7 +73,6 @@ fun UpdatesMenu(
|
||||
|
||||
@Composable
|
||||
fun UpdatesItem(
|
||||
serverUrl: String,
|
||||
chapterDownloadItem: ChapterDownloadItem,
|
||||
onClickItem: () -> Unit,
|
||||
onClickCover: () -> Unit,
|
||||
@@ -101,7 +100,8 @@ fun UpdatesItem(
|
||||
.padding(start = 16.dp, top = 8.dp, bottom = 8.dp)
|
||||
.clip(MaterialTheme.shapes.medium)
|
||||
.clickable { onClickCover() },
|
||||
imageUrl = manga.cover(serverUrl)
|
||||
cover = lazyPainterResource(manga, filterQuality = FilterQuality.Medium),
|
||||
contentDescription = manga.title
|
||||
)
|
||||
MangaListItemColumn(
|
||||
modifier = Modifier
|
||||
|
||||
@@ -8,11 +8,9 @@ package ca.gosyer.ui.updates
|
||||
|
||||
import ca.gosyer.data.download.DownloadService
|
||||
import ca.gosyer.data.models.Chapter
|
||||
import ca.gosyer.data.server.ServerPreferences
|
||||
import ca.gosyer.data.server.interactions.ChapterInteractionHandler
|
||||
import ca.gosyer.data.server.interactions.UpdatesInteractionHandler
|
||||
import ca.gosyer.ui.base.components.ChapterDownloadItem
|
||||
import ca.gosyer.ui.base.prefs.asStateIn
|
||||
import ca.gosyer.ui.base.vm.ViewModel
|
||||
import ca.gosyer.util.lang.throwIfCancellation
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -26,10 +24,8 @@ import javax.inject.Inject
|
||||
class UpdatesMenuViewModel @Inject constructor(
|
||||
private val chapterHandler: ChapterInteractionHandler,
|
||||
private val updatesHandler: UpdatesInteractionHandler,
|
||||
private val serverPreferences: ServerPreferences,
|
||||
private val downloadService: DownloadService
|
||||
) : ViewModel() {
|
||||
val serverUrl = serverPreferences.serverUrl().asStateIn(scope).asStateFlow()
|
||||
|
||||
private val _isLoading = MutableStateFlow(true)
|
||||
val isLoading = _isLoading.asStateFlow()
|
||||
|
||||
Reference in New Issue
Block a user