Reader updates (#800)

* Refine

* Bars and sliders

* Zoomable

* Refine

* Refine Slider & LazyList state sync

* Refine

* Cleanup

* Fixes

* Fix initial state

* Fixes

* Fixes

* Fixup initial state

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
This commit is contained in:
Vladimir DostoyevskiyandGG Bond authored and GitHub committed 2024-02-27 23:14:36 +08:00
1 parent bc3c4e969c
commit 31f66fd05b
5 files changed
+124 -37

No files matched your search

+3
View File
@@ -245,6 +245,7 @@ dependencies {
implementation(libs.aboutlibraries.core)
implementation(libs.aboutlibraries.compose.m3)
implementation(libs.accompanist.drawable.painter)
implementation(libs.insetter) // Dead Dependency
@@ -257,6 +258,8 @@ dependencies {
implementation(platform(libs.coil.bom))
implementation(libs.bundles.coil)
implementation(libs.telephoto.zoomable)
implementation(libs.bundles.ktor)
implementation(libs.bundles.kotlinx.serialization)
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
@@ -22,11 +23,18 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.LayoutDirection.Rtl
import androidx.compose.ui.zIndex
import coil3.BitmapImage
import coil3.DrawableImage
import coil3.Image as CoilImage
import com.google.accompanist.drawablepainter.DrawablePainter
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.data.BaseGalleryInfo
import com.hippo.ehviewer.collectAsState
import com.hippo.ehviewer.download.DownloadManager
import com.hippo.ehviewer.download.archiveFile
import com.hippo.ehviewer.gallery.ArchivePageLoader
@@ -39,6 +47,11 @@ import eu.kanade.tachiyomi.source.model.Page.State.ERROR
import eu.kanade.tachiyomi.source.model.Page.State.LOAD_PAGE
import eu.kanade.tachiyomi.source.model.Page.State.QUEUE
import eu.kanade.tachiyomi.source.model.Page.State.READY
import eu.kanade.tachiyomi.ui.reader.ReaderAppBars
import me.saket.telephoto.zoomable.ZoomSpec
import me.saket.telephoto.zoomable.rememberZoomableState
import me.saket.telephoto.zoomable.zoomable
import moe.tarsin.kt.unreachable
@Destination
@Composable
@@ -54,45 +67,63 @@ fun ReaderScreen(info: BaseGalleryInfo, page: Int = -1) {
pageLoader.stop()
}
}
val showSeekbar by Settings.showReaderSeekbar.collectAsState()
val lazyListState = rememberLazyListState()
val zoomableState = rememberZoomableState(zoomSpec = ZoomSpec(maxZoomFactor = 3f))
val sync = remember { SliderPagerDoubleSync(lazyListState) }
sync.Sync()
val isRtl = LocalLayoutDirection.current == Rtl
Deferred({ pageLoader.awaitReady() }) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
items(pageLoader.mPages) { page ->
pageLoader.request(page.index)
val state by page.status.collectAsState()
when (state) {
QUEUE, LOAD_PAGE -> {
Box(modifier = Modifier.fillMaxWidth().aspectRatio(DEFAULT_ASPECT)) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
Box {
ReaderAppBars(
visible = true,
isRtl = isRtl,
showSeekBar = showSeekbar,
currentPage = sync.sliderValue,
totalPages = pageLoader.size,
onSliderValueChange = { sync.sliderScrollTo(it + 1) },
onClickSettings = { },
modifier = Modifier.zIndex(1f),
)
LazyColumn(modifier = Modifier.fillMaxSize().zoomable(zoomableState), state = lazyListState) {
items(pageLoader.mPages) { page ->
pageLoader.request(page.index)
val state by page.status.collectAsState()
when (state) {
QUEUE, LOAD_PAGE -> {
Box(modifier = Modifier.fillMaxWidth().aspectRatio(DEFAULT_ASPECT)) {
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
}
}
}
DOWNLOAD_IMAGE -> {
Box(modifier = Modifier.fillMaxWidth().aspectRatio(DEFAULT_ASPECT)) {
val progress by page.progressFlow.collectAsState()
CircularProgressIndicator(
progress = { progress / 100f },
modifier = Modifier.align(Alignment.Center),
DOWNLOAD_IMAGE -> {
Box(modifier = Modifier.fillMaxWidth().aspectRatio(DEFAULT_ASPECT)) {
val progress by page.progressFlow.collectAsState()
CircularProgressIndicator(
progress = { progress / 100f },
modifier = Modifier.align(Alignment.Center),
)
}
}
READY -> {
val image = page.image!!.innerImage!!
val painter = remember(image) { image.toPainter() }
Image(
painter = painter,
contentDescription = null,
modifier = Modifier.fillMaxWidth(),
contentScale = ContentScale.FillWidth,
)
}
}
READY -> {
val image = page.image!!.innerImage!!
val painter = remember(image) { image.toPainter() }
Image(
painter = painter,
contentDescription = null,
modifier = Modifier.fillMaxWidth(),
contentScale = ContentScale.FillWidth,
)
}
ERROR -> {
Box(modifier = Modifier.fillMaxWidth().aspectRatio(DEFAULT_ASPECT)) {
Column(
modifier = Modifier.align(Companion.Center),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(text = page.errorMsg.orEmpty())
Button(onClick = { }) {
Text(text = stringResource(id = R.string.action_retry))
ERROR -> {
Box(modifier = Modifier.fillMaxWidth().aspectRatio(DEFAULT_ASPECT)) {
Column(
modifier = Modifier.align(Companion.Center),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(text = page.errorMsg.orEmpty())
Button(onClick = { }) {
Text(text = stringResource(id = R.string.action_retry))
}
}
}
}
@@ -105,7 +136,8 @@ fun ReaderScreen(info: BaseGalleryInfo, page: Int = -1) {
private fun CoilImage.toPainter() = when (this) {
is BitmapImage -> BitmapPainter(image = bitmap.asImageBitmap())
else -> TODO()
is DrawableImage -> DrawablePainter(drawable)
else -> unreachable()
}
private const val DEFAULT_ASPECT = 1 / 1.4125f
@@ -0,0 +1,46 @@
package com.hippo.ehviewer.ui.reader
import androidx.compose.foundation.interaction.collectIsDraggedAsState
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.mapNotNull
class SliderPagerDoubleSync(private val lazyListState: LazyListState) {
private var sliderFollowPager by mutableStateOf(true)
var sliderValue by mutableIntStateOf(1)
fun sliderScrollTo(index: Int) {
sliderFollowPager = false
sliderValue = index
}
@Composable
fun Sync() {
val fling by lazyListState.interactionSource.collectIsDraggedAsState()
if (fling) sliderFollowPager = true
if (sliderFollowPager) {
LaunchedEffect(Unit) {
snapshotFlow {
lazyListState.layoutInfo
}.mapNotNull { info ->
info.visibleItemsInfo.lastOrNull()
}.collect { info ->
sliderValue = info.index + 1
}
}
} else {
LaunchedEffect(Unit) {
snapshotFlow { sliderValue - 1 }.collectLatest { index ->
lazyListState.animateScrollToItem(index)
}
}
}
}
}
@@ -24,7 +24,8 @@ fun ReaderAppBars(
totalPages: Int,
onSliderValueChange: (Int) -> Unit,
onClickSettings: () -> Unit,
) = Column(modifier = Modifier.fillMaxHeight(), verticalArrangement = Arrangement.SpaceBetween) {
modifier: Modifier = Modifier,
) = Column(modifier = modifier.fillMaxHeight(), verticalArrangement = Arrangement.SpaceBetween) {
Spacer(modifier = Modifier.weight(1f))
AnimatedVisibility(
+5
View File
@@ -1,5 +1,6 @@
[versions]
aboutlibraries = "10.10.0"
accompanist = "0.34.0"
agp = "8.4.0-alpha11"
androidx-activity = "1.8.2"
androidx-appcompat = "1.6.1"
@@ -29,11 +30,13 @@ ktor = "3.0.0-beta-1"
okhttp3 = "5.0.0-alpha.12"
okio = "3.8.0"
splitties = "3.0.0"
telephoto = "0.8.0"
xmlutil = "0.86.3"
[libraries]
aboutlibraries-core = { module = "com.mikepenz:aboutlibraries-core", version.ref = "aboutlibraries" }
aboutlibraries-compose-m3 = { module = "com.mikepenz:aboutlibraries-compose-m3", version.ref = "aboutlibraries" }
accompanist-drawable-painter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanist" }
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
@@ -117,6 +120,8 @@ splitties-arch-room = { module = "com.louiscad.splitties:splitties-arch-room", v
splitties-preferences = { module = "com.louiscad.splitties:splitties-preferences", version.ref = "splitties" }
splitties-systemservices = { module = "com.louiscad.splitties:splitties-systemservices", version.ref = "splitties" }
telephoto-zoomable = { module = "me.saket.telephoto:zoomable", version.ref = "telephoto" }
[bundles]
coil = ["coil-compose", "coil-gif", "coil-network"]
compose = ["compose-foundation", "compose-material-icons-extended", "compose-material3", "compose-material3-windowsizeclass", "compose-ui-util"]