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:
1 parent
bc3c4e969c
commit
31f66fd05b
5 files changed
+124
-37
No files matched your search
@@ -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(
|
||||
|
||||
@@ -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"]
|
||||
|
||||
Reference in new issue
Block a user