Просмотр исходного кода

fix: align manga chapter navigation and sheets

HapeLee 3 месяцев назад
Родитель
Сommit
525174d54e

+ 6 - 4
app/src/main/java/io/legado/app/data/repository/MangaReaderSessionRepository.kt

@@ -358,13 +358,15 @@ class MangaReaderSessionRepository(
         postEvent(EventBus.SOURCE_CHANGED, book.bookUrl)
     }
 
-    fun openChapter(index: Int, pageIndex: Int = 0) {
-        if (index >= ReadManga.chapterSize) return
+    suspend fun openChapter(index: Int, pageIndex: Int = 0) = withContext(Dispatchers.IO) {
+        val currentBook = ReadManga.book ?: return@withContext
+        database.bookDao.getBook(currentBook.bookUrl)?.let(ReadManga::upData)
+        if (index !in 0 until ReadManga.simulatedChapterSize) return@withContext
         ReadManga.showLoading()
         ReadManga.durChapterIndex = index
-        ReadManga.durChapterPos = pageIndex
-        ReadManga.saveRead()
+        ReadManga.durChapterPos = pageIndex.coerceAtLeast(0)
         ReadManga.loadContent()
+        ReadManga.saveRead(pageChanged = true)
     }
 
     suspend fun removeCurrentTemporaryBook() = withContext(Dispatchers.IO) {

+ 1 - 1
app/src/main/java/io/legado/app/ui/book/manga/MangaReaderContract.kt

@@ -180,6 +180,7 @@ sealed interface MangaReaderIntent {
 }
 
 sealed interface MangaReaderSheet {
+    data object Catalog : MangaReaderSheet
     data object Reader : MangaReaderSheet
     data object AutoRead : MangaReaderSheet
     data object ColorFilter : MangaReaderSheet
@@ -232,7 +233,6 @@ enum class MangaReaderSettingKey {
 
 sealed interface MangaReaderEffect {
     data class Finish(val bookshelfChanged: Boolean = false) : MangaReaderEffect
-    data object OpenCatalog : MangaReaderEffect
     data object OpenBookInfo : MangaReaderEffect
     data class OpenChapterUrl(val externalBrowser: Boolean) : MangaReaderEffect
     data class OpenSourceLogin(val sourceUrl: String) : MangaReaderEffect

+ 18 - 10
app/src/main/java/io/legado/app/ui/book/manga/MangaReaderRouteScreen.kt

@@ -2,7 +2,6 @@ package io.legado.app.ui.book.manga
 
 import android.view.KeyEvent
 import android.view.WindowManager
-import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.DisposableEffect
 import androidx.compose.runtime.LaunchedEffect
@@ -14,7 +13,8 @@ import androidx.lifecycle.LifecycleEventObserver
 import androidx.lifecycle.compose.LocalLifecycleOwner
 import androidx.lifecycle.compose.collectAsStateWithLifecycle
 import io.legado.app.receiver.NetworkChangedListener
-import io.legado.app.ui.book.toc.TocActivityResult
+import io.legado.app.ui.book.read.sheet.ReaderBookSheetRoute
+import io.legado.app.ui.book.read.sheet.ReaderBookSheetTab
 import io.legado.app.ui.main.MainActivity
 import io.legado.app.utils.NetworkUtils
 import io.legado.app.utils.openUrl
@@ -44,11 +44,6 @@ fun MangaReaderRouteScreen(
     val lifecycleOwner = LocalLifecycleOwner.current
     val state by viewModel.uiState.collectAsStateWithLifecycle()
     val networkChangedListener = remember(activity) { NetworkChangedListener(activity) }
-    val tocLauncher = rememberLauncherForActivityResult(TocActivityResult()) { result ->
-        result?.let { (chapterIndex, pageIndex) ->
-            viewModel.onIntent(MangaReaderIntent.OpenChapter(chapterIndex, pageIndex))
-        }
-    }
 
     LaunchedEffect(viewModel, bookUrl, inBookshelf, chapterChanged) {
         viewModel.onIntent(
@@ -61,9 +56,6 @@ fun MangaReaderRouteScreen(
             val currentState = viewModel.uiState.value
             when (effect) {
                 is MangaReaderEffect.Finish -> onFinish(effect.bookshelfChanged)
-                MangaReaderEffect.OpenCatalog -> currentState.bookUrl
-                    .takeIf(String::isNotEmpty)
-                    ?.let(tocLauncher::launch)
                 MangaReaderEffect.OpenBookInfo -> {
                     if (currentState.bookUrl.isNotEmpty()) {
                         onOpenBookInfo(
@@ -169,4 +161,20 @@ fun MangaReaderRouteScreen(
     }
 
     MangaReaderScreen(state = state, onIntent = viewModel::onIntent)
+    if (state.activeSheet == MangaReaderSheet.Catalog && state.bookUrl.isNotEmpty()) {
+        ReaderBookSheetRoute(
+            show = true,
+            bookUrl = state.bookUrl,
+            initialTab = ReaderBookSheetTab.Toc,
+            onDismissRequest = { viewModel.onIntent(MangaReaderIntent.DismissSheet) },
+            onChapterClick = { chapterIndex, pageIndex ->
+                viewModel.onIntent(MangaReaderIntent.DismissSheet)
+                viewModel.onIntent(MangaReaderIntent.OpenChapter(chapterIndex, pageIndex))
+            },
+            onOpenFullBookInfo = {
+                viewModel.onIntent(MangaReaderIntent.DismissSheet)
+                onOpenBookInfo(state.bookName, state.bookAuthor, state.bookUrl)
+            },
+        )
+    }
 }

+ 3 - 1
app/src/main/java/io/legado/app/ui/book/manga/MangaReaderScreen.kt

@@ -153,7 +153,9 @@ fun MangaReaderScreen(
             ReaderStatusOverlay(state, onIntent)
         }
     }
-    state.activeSheet?.takeIf { it != MangaReaderSheet.ChangeSource }?.let { sheet ->
+    state.activeSheet?.takeIf {
+        it != MangaReaderSheet.ChangeSource && it != MangaReaderSheet.Catalog
+    }?.let { sheet ->
         MangaReaderSettingsSheet(sheet, state, onIntent)
     }
     if (state.activeSheet == MangaReaderSheet.ChangeSource) {

+ 80 - 50
app/src/main/java/io/legado/app/ui/book/manga/MangaReaderSheets.kt

@@ -9,15 +9,9 @@ import androidx.compose.foundation.layout.height
 import androidx.compose.foundation.rememberScrollState
 import androidx.compose.foundation.verticalScroll
 import androidx.compose.material3.MaterialTheme
-import androidx.compose.material3.Slider
-import androidx.compose.material3.Switch
 import androidx.compose.material3.Text
 import androidx.compose.material3.TextButton
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableFloatStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.setValue
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.res.stringResource
@@ -25,6 +19,11 @@ import androidx.compose.ui.unit.dp
 import io.legado.app.R
 import io.legado.app.ui.book.manga.config.MangaScrollMode
 import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
+import io.legado.app.ui.widget.components.card.SettingCard
+import io.legado.app.ui.widget.components.settingItem.SliderSettingItem
+import io.legado.app.ui.widget.components.settingItem.SwitchSettingItem
+import io.legado.app.ui.widget.components.settingItem.DropdownListSettingItem
+import io.legado.app.ui.widget.components.settingItem.ClickableSettingItem
 import kotlin.math.roundToInt
 
 @Composable
@@ -38,6 +37,7 @@ internal fun MangaReaderSettingsSheet(
         show = true,
         onDismissRequest = { onIntent(MangaReaderIntent.DismissSheet) },
         title = when (sheet) {
+            MangaReaderSheet.Catalog -> stringResource(R.string.chapter_list)
             MangaReaderSheet.Reader -> stringResource(R.string.manga_setting)
             MangaReaderSheet.AutoRead -> stringResource(R.string.manga_reader_auto_read)
             MangaReaderSheet.ColorFilter -> stringResource(R.string.manga_reader_display_filter)
@@ -52,20 +52,39 @@ internal fun MangaReaderSettingsSheet(
                 .verticalScroll(rememberScrollState()),
             verticalArrangement = Arrangement.spacedBy(8.dp),
         ) {
+            SettingCard(
+                modifier = Modifier.fillMaxWidth(),
+                cornerRadius = 16.dp,
+            ) {
             when (sheet) {
+                MangaReaderSheet.Catalog -> Unit
                 MangaReaderSheet.Reader -> {
-                    Text(stringResource(R.string.read_type), style = MaterialTheme.typography.titleMedium)
-                    listOf(
-                        MangaScrollMode.WEBTOON to stringResource(R.string.webtoon),
-                        MangaScrollMode.WEBTOON_WITH_GAP to stringResource(R.string.manga_reader_webtoon_gap),
-                        MangaScrollMode.PAGE_LEFT_TO_RIGHT to stringResource(R.string.manga_reader_left_to_right),
-                        MangaScrollMode.PAGE_RIGHT_TO_LEFT to stringResource(R.string.manga_reader_right_to_left),
-                        MangaScrollMode.PAGE_TOP_TO_BOTTOM to stringResource(R.string.manga_reader_top_to_bottom),
-                    ).forEach { (mode, label) ->
-                        TextButton(onClick = {
-                            onIntent(MangaReaderIntent.UpdateSetting(MangaReaderSettingKey.SCROLL_MODE, mode))
-                        }) { Text(if (settings.scrollMode == mode) "✓ $label" else label) }
-                    }
+                    DropdownListSettingItem(
+                        title = stringResource(R.string.read_type),
+                        selectedValue = settings.scrollMode.toString(),
+                        displayEntries = arrayOf(
+                            stringResource(R.string.webtoon),
+                            stringResource(R.string.manga_reader_webtoon_gap),
+                            stringResource(R.string.manga_reader_left_to_right),
+                            stringResource(R.string.manga_reader_right_to_left),
+                            stringResource(R.string.manga_reader_top_to_bottom),
+                        ),
+                        entryValues = arrayOf(
+                            MangaScrollMode.WEBTOON,
+                            MangaScrollMode.WEBTOON_WITH_GAP,
+                            MangaScrollMode.PAGE_LEFT_TO_RIGHT,
+                            MangaScrollMode.PAGE_RIGHT_TO_LEFT,
+                            MangaScrollMode.PAGE_TOP_TO_BOTTOM,
+                        ).map(Int::toString).toTypedArray(),
+                        onValueChange = {
+                            onIntent(
+                                MangaReaderIntent.UpdateSetting(
+                                    MangaReaderSettingKey.SCROLL_MODE,
+                                    it.toInt(),
+                                )
+                            )
+                        },
+                    )
                     SettingSlider(stringResource(R.string.manga_reader_side_padding), settings.sidePaddingPercent, 0..45) {
                         onIntent(MangaReaderIntent.UpdateSetting(MangaReaderSettingKey.SIDE_PADDING, it))
                     }
@@ -115,15 +134,18 @@ internal fun MangaReaderSettingsSheet(
                     SettingSwitch(stringResource(R.string.manga_reader_hide_chapter_label), settings.hideChapterLabel) { updateBoolean(onIntent, MangaReaderSettingKey.HIDE_CHAPTER_LABEL, it) }
                     SettingSwitch(stringResource(R.string.manga_reader_hide_total_progress), settings.hideProgress) { updateBoolean(onIntent, MangaReaderSettingKey.HIDE_PROGRESS, it) }
                     SettingSwitch(stringResource(R.string.manga_reader_hide_progress_label), settings.hideProgressLabel) { updateBoolean(onIntent, MangaReaderSettingKey.HIDE_PROGRESS_LABEL, it) }
-                    Text(stringResource(R.string.manga_reader_footer_alignment), style = MaterialTheme.typography.titleMedium)
-                    TextButton(onClick = { updateInt(onIntent, MangaReaderSettingKey.FOOTER_ALIGNMENT, 0) }) {
-                        val label = stringResource(R.string.manga_reader_left_align)
-                        Text(if (settings.footerAlignment == 0) "✓ $label" else label)
-                    }
-                    TextButton(onClick = { updateInt(onIntent, MangaReaderSettingKey.FOOTER_ALIGNMENT, 1) }) {
-                        val label = stringResource(R.string.manga_reader_center_align)
-                        Text(if (settings.footerAlignment == 1) "✓ $label" else label)
-                    }
+                    DropdownListSettingItem(
+                        title = stringResource(R.string.manga_reader_footer_alignment),
+                        selectedValue = settings.footerAlignment.toString(),
+                        displayEntries = arrayOf(
+                            stringResource(R.string.manga_reader_left_align),
+                            stringResource(R.string.manga_reader_center_align),
+                        ),
+                        entryValues = arrayOf("0", "1"),
+                        onValueChange = {
+                            updateInt(onIntent, MangaReaderSettingKey.FOOTER_ALIGNMENT, it.toInt())
+                        },
+                    )
                 }
                 MangaReaderSheet.AutoRead -> {
                     SettingSwitch(stringResource(R.string.manga_reader_enable_auto_read), state.autoReadEnabled) {
@@ -173,15 +195,28 @@ internal fun MangaReaderSettingsSheet(
                 }
                 MangaReaderSheet.ChangeSource -> Unit
                 MangaReaderSheet.SourceActions -> {
-                    TextButton(onClick = { onIntent(MangaReaderIntent.OpenSourceLogin) }) { Text(stringResource(R.string.login)) }
-                    TextButton(onClick = { onIntent(MangaReaderIntent.RequestPayCurrentChapter) }) { Text(stringResource(R.string.manga_reader_buy_chapter)) }
-                    TextButton(onClick = { onIntent(MangaReaderIntent.OpenSourceEdit) }) { Text(stringResource(R.string.edit_source)) }
-                    TextButton(onClick = {
+                    ClickableSettingItem(
+                        title = stringResource(R.string.login),
+                        onClick = { onIntent(MangaReaderIntent.OpenSourceLogin) },
+                    )
+                    ClickableSettingItem(
+                        title = stringResource(R.string.manga_reader_buy_chapter),
+                        onClick = { onIntent(MangaReaderIntent.RequestPayCurrentChapter) },
+                    )
+                    ClickableSettingItem(
+                        title = stringResource(R.string.edit_source),
+                        onClick = { onIntent(MangaReaderIntent.OpenSourceEdit) },
+                    )
+                    ClickableSettingItem(
+                        title = stringResource(R.string.disable_source),
+                        onClick = {
                         onIntent(MangaReaderIntent.DisableCurrentSource)
                         onIntent(MangaReaderIntent.DismissSheet)
-                    }) { Text(stringResource(R.string.disable_source)) }
+                        },
+                    )
                 }
             }
+            }
             Spacer(Modifier.height(24.dp))
         }
     }
@@ -203,26 +238,21 @@ private fun updateInt(
 
 @Composable
 private fun SettingSwitch(label: String, checked: Boolean, onCheckedChange: (Boolean) -> Unit) {
-    Row(
-        Modifier.fillMaxWidth(),
-        verticalAlignment = Alignment.CenterVertically,
-        horizontalArrangement = Arrangement.SpaceBetween,
-    ) {
-        Text(label, modifier = Modifier.weight(1f))
-        Switch(checked = checked, onCheckedChange = onCheckedChange)
-    }
+    SwitchSettingItem(
+        title = label,
+        checked = checked,
+        onCheckedChange = onCheckedChange,
+    )
 }
 
 @Composable
 private fun SettingSlider(label: String, value: Int, range: IntRange, onValueChange: (Int) -> Unit) {
-    var pending by remember(value) { mutableFloatStateOf(value.toFloat()) }
-    Column {
-        Text("$label: ${pending.toInt()}")
-        Slider(
-            value = pending.coerceIn(range.first.toFloat(), range.last.toFloat()),
-            onValueChange = { pending = it },
-            onValueChangeFinished = { onValueChange(pending.toInt()) },
-            valueRange = range.first.toFloat()..range.last.toFloat(),
-        )
-    }
+    SliderSettingItem(
+        title = label,
+        value = value.toFloat(),
+        defaultValue = range.first.toFloat(),
+        valueRange = range.first.toFloat()..range.last.toFloat(),
+        valueLabel = { it.toInt().toString() },
+        onValueChange = { onValueChange(it.toInt()) },
+    )
 }

+ 4 - 5
app/src/main/java/io/legado/app/ui/book/manga/MangaReaderViewModel.kt

@@ -99,10 +99,9 @@ class MangaReaderViewModel(
             }
             MangaReaderIntent.ReloadContent -> sessionRepository.reloadOrUpdateContent()
             is MangaReaderIntent.ApplyReadingProgress -> sessionRepository.applyProgress(intent.progress)
-            is MangaReaderIntent.OpenChapter -> sessionRepository.openChapter(
-                intent.chapterIndex,
-                intent.pageIndex,
-            )
+            is MangaReaderIntent.OpenChapter -> launchAction {
+                sessionRepository.openChapter(intent.chapterIndex, intent.pageIndex)
+            }
             is MangaReaderIntent.ChangeSourceBook -> launchAction {
                 showLoading()
                 sessionRepository.changeSource(intent.book, intent.toc)
@@ -171,7 +170,7 @@ class MangaReaderViewModel(
             }
             MangaReaderIntent.PreviousChapter -> sessionRepository.moveToPreviousChapter(true)
             MangaReaderIntent.NextChapter -> sessionRepository.moveToNextChapter(true)
-            MangaReaderIntent.OpenCatalog -> emitAndHide(MangaReaderEffect.OpenCatalog)
+            MangaReaderIntent.OpenCatalog -> showSheet(MangaReaderSheet.Catalog)
             MangaReaderIntent.OpenBookInfo -> emitAndHide(MangaReaderEffect.OpenBookInfo)
             MangaReaderIntent.OpenChapterUrl -> emitAndHide(
                 MangaReaderEffect.OpenChapterUrl(readSettingsGateway.currentSettings.readUrlInBrowser)

+ 18 - 12
app/src/main/java/io/legado/app/ui/book/manga/ReadMangaActivity.kt

@@ -13,7 +13,8 @@ import io.legado.app.base.BaseComposeActivity
 import io.legado.app.receiver.NetworkChangedListener
 import io.legado.app.ui.book.info.BookInfoActivity
 import io.legado.app.ui.book.info.READER_RESULT_DELETED
-import io.legado.app.ui.book.toc.TocActivityResult
+import io.legado.app.ui.book.read.sheet.ReaderBookSheetRoute
+import io.legado.app.ui.book.read.sheet.ReaderBookSheetTab
 import io.legado.app.ui.login.SourceLoginType
 import io.legado.app.ui.main.MainActivity
 import io.legado.app.utils.NetworkUtils
@@ -45,12 +46,6 @@ class ReadMangaActivity : BaseComposeActivity(imageBg = false) {
             }
         }
 
-    private val tocActivity = registerForActivityResult(TocActivityResult()) {
-        it?.let { (chapterIndex, pageIndex) ->
-            readerViewModel.onIntent(MangaReaderIntent.OpenChapter(chapterIndex, pageIndex))
-        }
-    }
-
     private val bookInfoActivity =
         registerForActivityResult(StartActivityContract(BookInfoActivity::class.java)) {
             if (it.resultCode == RESULT_OK) {
@@ -76,6 +71,22 @@ class ReadMangaActivity : BaseComposeActivity(imageBg = false) {
             readerViewModel.effects.collectLatest(::handleEffect)
         }
         MangaReaderScreen(state = state, onIntent = readerViewModel::onIntent)
+        if (state.activeSheet == MangaReaderSheet.Catalog && state.bookUrl.isNotEmpty()) {
+            ReaderBookSheetRoute(
+                show = true,
+                bookUrl = state.bookUrl,
+                initialTab = ReaderBookSheetTab.Toc,
+                onDismissRequest = { readerViewModel.onIntent(MangaReaderIntent.DismissSheet) },
+                onChapterClick = { chapterIndex, pageIndex ->
+                    readerViewModel.onIntent(MangaReaderIntent.DismissSheet)
+                    readerViewModel.onIntent(MangaReaderIntent.OpenChapter(chapterIndex, pageIndex))
+                },
+                onOpenFullBookInfo = {
+                    readerViewModel.onIntent(MangaReaderIntent.DismissSheet)
+                    openBookInfoActivity()
+                },
+            )
+        }
     }
 
     private fun handleEffect(effect: MangaReaderEffect) {
@@ -84,7 +95,6 @@ class ReadMangaActivity : BaseComposeActivity(imageBg = false) {
                 if (effect.bookshelfChanged) setResult(RESULT_OK)
                 finishReader()
             }
-            MangaReaderEffect.OpenCatalog -> openCatalog()
             MangaReaderEffect.OpenBookInfo -> openBookInfoActivity()
             is MangaReaderEffect.OpenChapterUrl -> openCurrentChapterUrl(effect.externalBrowser)
             is MangaReaderEffect.OpenSourceLogin -> startActivity(
@@ -154,10 +164,6 @@ class ReadMangaActivity : BaseComposeActivity(imageBg = false) {
         else finish()
     }
 
-    private fun openCatalog() {
-        readerViewModel.uiState.value.bookUrl.takeIf(String::isNotEmpty)?.let(tocActivity::launch)
-    }
-
     private fun openBookInfoActivity() {
         readerViewModel.uiState.value.let {
             if (it.bookUrl.isEmpty()) return