From 05e78b82c7270f6124c33111ccc59ede80ccab6d Mon Sep 17 00:00:00 2001 From: DatLag Date: Mon, 13 Nov 2023 21:27:59 +0100 Subject: [PATCH] fixed series title and improved blurhash loading --- .../datlag/burningseries/other/StateSaver.kt | 9 ++++++ .../ui/custom/state/ErrorState.kt | 6 +++- .../ui/custom/state/LoadingState.kt | 6 +++- .../ui/screen/initial/home/HomeScreen.kt | 14 +++++++- .../ui/screen/initial/series/SeriesScreen.kt | 32 ++++++++++++++++++- .../initial/series/component/EpisodeItem.kt | 15 ++++----- .../network/scraper/BurningSeries.kt | 15 +++------ 7 files changed, 74 insertions(+), 23 deletions(-) create mode 100644 app/shared/src/commonMain/kotlin/dev/datlag/burningseries/other/StateSaver.kt diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/other/StateSaver.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/other/StateSaver.kt new file mode 100644 index 00000000..486776db --- /dev/null +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/other/StateSaver.kt @@ -0,0 +1,9 @@ +package dev.datlag.burningseries.other + +data object StateSaver { + var homeGridIndex: Int = 0 + var homeGridOffset: Int = 0 + + var seriesListIndex: Int = 0 + var seriesListOffset: Int = 0 +} \ No newline at end of file diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/state/ErrorState.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/state/ErrorState.kt index 63604ed0..a4e907d9 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/state/ErrorState.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/state/ErrorState.kt @@ -11,6 +11,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import dev.datlag.burningseries.LocalDarkMode import dev.datlag.burningseries.shared.SharedRes @@ -36,8 +37,11 @@ fun ErrorState(text: String, onRetry: () -> Unit) { contentDescription = text ) Text( + modifier = Modifier.fillMaxWidth(0.85F), text = text, - fontWeight = FontWeight.SemiBold + fontWeight = FontWeight.SemiBold, + softWrap = true, + textAlign = TextAlign.Center ) Button( onClick = { diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/state/LoadingState.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/state/LoadingState.kt index 1b1ab800..4c248b2c 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/state/LoadingState.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/custom/state/LoadingState.kt @@ -10,6 +10,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import dev.datlag.burningseries.LocalDarkMode import dev.datlag.burningseries.shared.SharedRes @@ -35,8 +36,11 @@ fun LoadingState(text: String) { contentDescription = text ) Text( + modifier = Modifier.fillMaxWidth(0.85F), text = text, - fontWeight = FontWeight.SemiBold + fontWeight = FontWeight.SemiBold, + softWrap = true, + textAlign = TextAlign.Center ) } } diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/home/HomeScreen.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/home/HomeScreen.kt index 1f7b9f00..ad7f6c7f 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/home/HomeScreen.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/home/HomeScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.material3.windowsizeclass.ExperimentalMaterial3WindowSiz import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass import androidx.compose.runtime.Composable +import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -27,6 +28,7 @@ import dev.datlag.burningseries.common.header import dev.datlag.burningseries.common.lifecycle.collectAsStateWithLifecycle import dev.datlag.burningseries.model.Home import dev.datlag.burningseries.model.state.HomeState +import dev.datlag.burningseries.other.StateSaver import dev.datlag.burningseries.shared.SharedRes import dev.datlag.burningseries.ui.custom.VerticalScrollbar import dev.datlag.burningseries.ui.custom.rememberScrollbarAdapter @@ -99,7 +101,10 @@ private fun MainView(home: Home, component: HomeComponent, modifier: Modifier = modifier = modifier.padding(horizontal = 16.dp), horizontalArrangement = Arrangement.spacedBy(2.dp) ) { - val state = rememberLazyGridState() + val state = rememberLazyGridState( + initialFirstVisibleItemIndex = StateSaver.homeGridIndex, + initialFirstVisibleItemScrollOffset = StateSaver.homeGridOffset + ) LazyVerticalGrid( columns = GridCells.Adaptive(400.dp), @@ -143,5 +148,12 @@ private fun MainView(home: Home, component: HomeComponent, modifier: Modifier = } } VerticalScrollbar(rememberScrollbarAdapter(state)) + + DisposableEffect(state) { + onDispose { + StateSaver.homeGridIndex = state.firstVisibleItemIndex + StateSaver.homeGridOffset = state.firstVisibleItemScrollOffset + } + } } } \ No newline at end of file diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/SeriesScreen.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/SeriesScreen.kt index 67cde6b6..944bc770 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/SeriesScreen.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/SeriesScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.* import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.CircleShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBackIos @@ -34,6 +35,7 @@ import dev.datlag.burningseries.common.onClick import dev.datlag.burningseries.model.BSUtil import dev.datlag.burningseries.model.Series import dev.datlag.burningseries.model.state.SeriesState +import dev.datlag.burningseries.other.StateSaver import dev.datlag.burningseries.shared.SharedRes import dev.datlag.burningseries.ui.custom.CountryImage import dev.datlag.burningseries.ui.custom.DefaultCollapsingToolbar @@ -42,6 +44,8 @@ import dev.datlag.burningseries.ui.custom.readmore.ReadMoreTextOverflow import dev.datlag.burningseries.ui.custom.readmore.ToggleArea import dev.datlag.burningseries.ui.custom.state.ErrorState import dev.datlag.burningseries.ui.custom.state.LoadingState +import dev.datlag.burningseries.ui.custom.toolbar.rememberCollapsingToolbarScaffoldState +import dev.datlag.burningseries.ui.custom.toolbar.rememberCollapsingToolbarState import dev.datlag.burningseries.ui.screen.initial.series.component.DescriptionText import dev.datlag.burningseries.ui.screen.initial.series.component.EpisodeItem import dev.datlag.burningseries.ui.screen.initial.series.component.SeasonAndLanguageButtons @@ -192,7 +196,13 @@ private fun CompactScreen(component: SeriesComponent) { } } is SeriesState.Success -> { + val state = rememberLazyListState( + initialFirstVisibleItemIndex = StateSaver.seriesListIndex, + initialFirstVisibleItemScrollOffset = StateSaver.seriesListOffset + ) + LazyColumn( + state = state, modifier = Modifier.fillMaxSize(), contentPadding = PaddingValues(horizontal = 16.dp) ) { @@ -214,6 +224,13 @@ private fun CompactScreen(component: SeriesComponent) { SeriesContent(current.series) } + + DisposableEffect(state) { + onDispose { + StateSaver.seriesListIndex = state.firstVisibleItemIndex + StateSaver.seriesListOffset = state.firstVisibleItemScrollOffset + } + } } } } @@ -237,7 +254,13 @@ private fun DefaultScreen(component: SeriesComponent) { } } is SeriesState.Success -> { + val state = rememberLazyListState( + initialFirstVisibleItemIndex = StateSaver.seriesListIndex, + initialFirstVisibleItemScrollOffset = StateSaver.seriesListOffset + ) + LazyColumn( + state = state, modifier = Modifier.fillMaxWidth(), contentPadding = PaddingValues(16.dp) ) { @@ -302,12 +325,19 @@ private fun DefaultScreen(component: SeriesComponent) { SeriesContent(current.series) } + + DisposableEffect(state) { + onDispose { + StateSaver.seriesListIndex = state.firstVisibleItemIndex + StateSaver.seriesListOffset = state.firstVisibleItemScrollOffset + } + } } } } private fun LazyListScope.SeriesContent(content: Series) { - items(content.episodes) { episode -> + items(content.episodes, key = { it.href }) { episode -> EpisodeItem(episode) } } \ No newline at end of file diff --git a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/component/EpisodeItem.kt b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/component/EpisodeItem.kt index 727dd390..03586ead 100644 --- a/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/component/EpisodeItem.kt +++ b/app/shared/src/commonMain/kotlin/dev/datlag/burningseries/ui/screen/initial/series/component/EpisodeItem.kt @@ -44,17 +44,14 @@ fun EpisodeItem(content: Series.Episode) { .clip(MaterialTheme.shapes.medium), contentAlignment = Alignment.Center ) { - var imageSize: Size by remember { mutableStateOf(DpSize(175.dp, 100.dp).toSize()) } - val imageBitmap: ImageBitmap? = remember(imageSize) { - - BlurHash.decode(hash = blurHash, width = imageSize.width.roundToInt(), height = imageSize.height.roundToInt()) - } - val modifier = Modifier.fillMaxSize().onSizeChanged { - imageSize = it.toSize() + val imageBitmap: ImageBitmap? by produceState(null) { + value = withIOContext { + BlurHash.decode(hash = blurHash, width = 175.dp.value.roundToInt(), height = 100.dp.value.roundToInt()) + } } imageBitmap?.let { Image( - modifier = modifier, + modifier = Modifier.fillMaxSize(), bitmap = it, contentDescription = content.title ) @@ -65,7 +62,7 @@ fun EpisodeItem(content: Series.Episode) { tint = Color.White ) } ?: run { - Box(modifier.background(MaterialTheme.colorScheme.primaryContainer)) + Box(Modifier.fillMaxSize().background(MaterialTheme.colorScheme.primaryContainer)) } } Text( diff --git a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/scraper/BurningSeries.kt b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/scraper/BurningSeries.kt index 545e4e9b..705ee63c 100644 --- a/network/src/commonMain/kotlin/dev/datlag/burningseries/network/scraper/BurningSeries.kt +++ b/network/src/commonMain/kotlin/dev/datlag/burningseries/network/scraper/BurningSeries.kt @@ -116,7 +116,9 @@ data object BurningSeries { val docHref = BSUtil.fixSeriesHref(href) val doc = getDocument(client, docHref) ?: return null - val title = doc.querySelector(".serie")?.querySelector("h2")?.textContent() ?: String() + val titleElement = doc.querySelector(".serie")?.querySelector("h2") ?: return null + val titleSeason = titleElement.querySelector("small")?.textContent()?.trim() + val title = titleElement.textContent().replace(titleSeason ?: String(), String()).trim() val description = doc.querySelector(".serie")?.querySelector("#sp_left > p")?.textContent() ?: String() val seasons = doc.querySelector(".serie")?.querySelector("#seasons")?.querySelector("ul")?.querySelectorAll("li")?.mapIndexed { index, it -> @@ -137,10 +139,6 @@ data object BurningSeries { ) } ?: emptyList() - val replacedTitle = - title.replace(Regex("(?:(\\n)*\\t)+", setOf(RegexOption.MULTILINE, RegexOption.IGNORE_CASE)), "\t") - val splitTitle = replacedTitle.trim().split("\t") - val selectedLanguageValue = doc.querySelector(".series-language")?.querySelector("option[selected]")?.getValue() var selectedLanguage: String? = null val languageElements = doc.querySelector(".series-language")?.querySelectorAll("option") ?: emptyList() @@ -169,9 +167,6 @@ data object BurningSeries { } } - val selectedSeason = if (splitTitle.size >= 2) splitTitle[1].trim() else seasons.firstOrNull()?.title ?: String() - val normalizedTitle = splitTitle[0].trim().replace(selectedSeason, String()).trim() - val episodesDoc = doc.querySelector(".serie")?.querySelector(".episodes")?.querySelectorAll("tr") ?: emptyList() val episodeInfoList = episodesDoc.mapNotNull { episodesElement -> val episodeList = episodesElement.querySelectorAll("td").mapNotNull { it.querySelector("a") }.mapNotNull { data -> @@ -215,11 +210,11 @@ data object BurningSeries { val (cover, isNsfw) = getCover(doc) return Series( - title = normalizedTitle.trim(), + title = title, description = description, coverHref = cover, href = docHref, - seasonTitle = selectedSeason.trim(), + seasonTitle = titleSeason ?: String(), seasons = seasons, selectedLanguage = selectedLanguage?.trim() ?: return null, languages = languages,