HapeLee 3 месяцев назад
Родитель
Сommit
d45998141e
1 измененных файлов с 208 добавлено и 163 удалено
  1. 208 163
      app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt

+ 208 - 163
app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt

@@ -3,12 +3,16 @@ package io.legado.app.ui.book.manage
 import androidx.activity.compose.BackHandler
 import androidx.activity.compose.rememberLauncherForActivityResult
 import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.animation.SizeTransform
 import androidx.compose.animation.animateColorAsState
 import androidx.compose.animation.core.FastOutSlowInEasing
 import androidx.compose.animation.core.tween
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.animation.togetherWith
 import androidx.compose.foundation.clickable
 import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.Column
 import androidx.compose.foundation.layout.ExperimentalLayoutApi
 import androidx.compose.foundation.layout.Row
@@ -38,6 +42,7 @@ import androidx.compose.material.icons.filled.PlayArrow
 import androidx.compose.material.icons.filled.Refresh
 import androidx.compose.material.icons.filled.Search
 import androidx.compose.material.icons.filled.SelectAll
+import androidx.compose.material.icons.filled.Settings
 import androidx.compose.material.icons.filled.Stop
 import androidx.compose.material.icons.filled.Upload
 import androidx.compose.material3.Checkbox
@@ -101,10 +106,11 @@ import io.legado.app.ui.widget.components.log.AppLogSheet
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenu
 import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
 import io.legado.app.ui.widget.components.modalBottomSheet.AppModalBottomSheet
-import io.legado.app.ui.widget.components.modalBottomSheet.OptionCard
-import io.legado.app.ui.widget.components.modalBottomSheet.OptionSheet
 import io.legado.app.ui.widget.components.progressIndicator.AppCircularProgressIndicator
 import io.legado.app.ui.widget.components.reorderAccessibility
+import io.legado.app.ui.widget.components.settingItem.TinyClickableSettingItem
+import io.legado.app.ui.widget.components.settingItem.TinyDropdownSettingItem
+import io.legado.app.ui.widget.components.settingItem.TinySwitchSettingItem
 import io.legado.app.ui.widget.components.text.AppText
 import io.legado.app.ui.widget.components.topbar.TopBarActionButton
 import io.legado.app.utils.ACache
@@ -157,7 +163,8 @@ private fun BookshelfManageScreen(
     var showFilePickerSheet by remember { mutableStateOf(false) }
     var showDownloadAllConfirmDialog by remember { mutableStateOf(false) }
     var showBatchDownloadConfirmDialog by remember { mutableStateOf(false) }
-    var showExportTypeDialog by remember { mutableStateOf(false) }
+    var showExportSheet by remember { mutableStateOf(false) }
+    var showExportSettings by remember { mutableStateOf(false) }
     var showExportFileNameDialog by remember { mutableStateOf(false) }
     var showCharsetDialog by remember { mutableStateOf(false) }
     var showLogSheet by remember { mutableStateOf(false) }
@@ -175,8 +182,8 @@ private fun BookshelfManageScreen(
     var pendingDeleteBookUrls by remember { mutableStateOf<Set<String>>(emptySet()) }
     var fabMenuExpanded by rememberSaveable { mutableStateOf(false) }
     var pendingExportBookUrl by remember { mutableStateOf<String?>(null) }
-    var pendingExportAll by remember { mutableStateOf(false) }
     var pendingExportSelection by remember { mutableStateOf<Set<String>>(emptySet()) }
+    var exportSheetBookUrls by remember { mutableStateOf<Set<String>>(emptySet()) }
     var customExportPath by remember { mutableStateOf("") }
     var customExportBook by remember { mutableStateOf<Book?>(null) }
     var customExportAllChapter by remember { mutableStateOf(false) }
@@ -205,6 +212,9 @@ private fun BookshelfManageScreen(
     val exportFileNameHintText = stringResource(R.string.export_file_name_template_hint)
     val exportFileNameHelpText = stringResource(R.string.export_file_name_template_help)
     val booksByUrl = remember(state.books) { state.books.associateBy { it.bookUrl } }
+    val exportSheetBooks = remember(exportSheetBookUrls, booksByUrl) {
+        exportSheetBookUrls.mapNotNull(booksByUrl::get)
+    }
     val userGroups = remember(state.groupList) { state.groupList.filter { it.groupId > 0L } }
 
     val groupNameResolver: (Book) -> String = remember(userGroups, noGroupText) {
@@ -291,12 +301,6 @@ private fun BookshelfManageScreen(
             }
             return@rememberLauncherForActivityResult
         }
-        if (pendingExportAll) {
-            state.books.forEach { book ->
-                startExport(context, dirPath, book, state.exportConfig.exportType)
-            }
-            return@rememberLauncherForActivityResult
-        }
         val bookUrl = pendingExportBookUrl ?: return@rememberLauncherForActivityResult
         val book = booksByUrl[bookUrl] ?: return@rememberLauncherForActivityResult
         if (state.exportConfig.isCustomEpubExportEnabled) {
@@ -327,22 +331,24 @@ private fun BookshelfManageScreen(
 
     fun selectExportFolder(
         bookUrl: String? = null,
-        forAll: Boolean = false,
         selection: Set<String> = emptySet()
     ) {
         pendingExportBookUrl = bookUrl
-        pendingExportAll = forAll
         pendingExportSelection = selection
         showFilePickerSheet = true
     }
 
-    fun exportBook(book: Book) {
+    fun startExportForBooks(books: List<Book>) {
+        if (books.isEmpty()) return
         val path = ACache.get().getAsString(exportBookPathKey)
         if (path.isNullOrEmpty() || !FileDoc.fromDir(path).checkWrite()) {
-            selectExportFolder(book.bookUrl)
-        } else if (state.exportConfig.isCustomEpubExportEnabled) {
+            when (books.size) {
+                1 -> selectExportFolder(books.single().bookUrl)
+                else -> selectExportFolder(selection = books.mapTo(linkedSetOf()) { it.bookUrl })
+            }
+        } else if (state.exportConfig.isCustomEpubExportEnabled && books.size == 1) {
             customExportPath = path
-            customExportBook = book
+            customExportBook = books.single()
             customExportAllChapter = false
             customEpubScopeInput = ""
             customEpubScopeError = null
@@ -350,33 +356,24 @@ private fun BookshelfManageScreen(
             customEpisodeExportNameInput = state.exportConfig.episodeExportFileName
             showCustomExportDialog = true
         } else {
-            startExport(context, path, book, state.exportConfig.exportType)
+            books.forEach { book ->
+                startExport(context, path, book, state.exportConfig.exportType)
+            }
         }
     }
 
+    fun showExportSheetFor(books: List<Book>) {
+        exportSheetBookUrls = books.mapTo(linkedSetOf()) { it.bookUrl }
+        showExportSettings = false
+        showExportSheet = exportSheetBookUrls.isNotEmpty()
+    }
+
     fun exportAll() {
-        val path = ACache.get().getAsString(exportBookPathKey)
-        if (path.isNullOrEmpty()) {
-            selectExportFolder(forAll = true)
-        } else {
-            state.books.forEach { book ->
-                startExport(context, path, book, state.exportConfig.exportType)
-            }
-        }
+        showExportSheetFor(state.books)
     }
 
     fun exportSelected() {
-        if (selectedBookUrls.isEmpty()) return
-        val path = ACache.get().getAsString(exportBookPathKey)
-        if (path.isNullOrEmpty() || !FileDoc.fromDir(path).checkWrite()) {
-            selectExportFolder(selection = selectedBookUrls)
-        } else {
-            selectedBookUrls.forEach { bookUrl ->
-                booksByUrl[bookUrl]?.let { book ->
-                    startExport(context, path, book, state.exportConfig.exportType)
-                }
-            }
-        }
+        showExportSheetFor(selectedBookUrls.mapNotNull(booksByUrl::get))
     }
     fun resolveSelectionGroupMask(): Long {
         val targetBooks = selectedBookUrls.mapNotNull { booksByUrl[it] }
@@ -502,7 +499,6 @@ private fun BookshelfManageScreen(
             }
         },
         dropDownMenuContent = { dismiss ->
-            var showCharsetMenu by remember { mutableStateOf(false) }
             RoundDropdownMenuItem(
                 text = stringResource(R.string.download_all),
                 onClick = {
@@ -519,118 +515,6 @@ private fun BookshelfManageScreen(
                 onClick = { dismiss(); exportAll() }
             )
             PillDivider()
-            RoundDropdownMenuItem(
-                text = stringResource(R.string.export_folder),
-                onClick = { dismiss(); selectExportFolder() }
-            )
-            RoundDropdownMenuItem(
-                text = stringResource(R.string.export_file_name),
-                onClick = {
-                    dismiss()
-                    exportFileNameInput = state.exportConfig.bookExportFileName.orEmpty()
-                    showExportFileNameDialog = true
-                }
-            )
-            RoundDropdownMenuItem(
-                text = "${stringResource(R.string.export_type)} (${exportTypes.getOrElse(state.exportConfig.exportType) { exportTypes[0] }})",
-                onClick = {
-                    dismiss()
-                    showExportTypeDialog = true
-                }
-            )
-            Box {
-                RoundDropdownMenuItem(
-                    text = "${stringResource(R.string.export_charset)} (${state.exportConfig.exportCharset})",
-                    onClick = { showCharsetMenu = true }
-                )
-                RoundDropdownMenu(
-                    expanded = showCharsetMenu,
-                    onDismissRequest = { showCharsetMenu = false }
-                ) { subDismiss ->
-                    commonCharsets.forEach { charset ->
-                        RoundDropdownMenuItem(
-                            text = charset,
-                            isSelected = state.exportConfig.exportCharset == charset,
-                            onClick = {
-                                viewModel.dispatch(BookshelfManageScreenIntent.SetExportCharset(charset))
-                                subDismiss()
-                                dismiss()
-                            }
-                        )
-                    }
-                    PillDivider()
-                    RoundDropdownMenuItem(
-                        text = "自定义...",
-                        onClick = {
-                            subDismiss()
-                            exportCharsetInput = state.exportConfig.exportCharset
-                            showCharsetDialog = true
-                        }
-                    )
-                }
-            }
-            PillDivider()
-            RoundDropdownMenuItem(
-                text = "替换净化",
-                isSelected = state.exportConfig.exportUseReplace,
-                onClick = {
-                    dismiss()
-                    viewModel.dispatch(
-                        BookshelfManageScreenIntent.SetExportUseReplace(!state.exportConfig.exportUseReplace)
-                    )
-                }
-            )
-            RoundDropdownMenuItem(
-                text = "自定义导出",
-                isSelected = state.exportConfig.enableCustomExport,
-                onClick = {
-                    dismiss()
-                    viewModel.dispatch(
-                        BookshelfManageScreenIntent.SetEnableCustomExport(!state.exportConfig.enableCustomExport)
-                    )
-                }
-            )
-            RoundDropdownMenuItem(
-                text = "导出包含章节名",
-                isSelected = !state.exportConfig.exportNoChapterName,
-                onClick = {
-                    dismiss()
-                    viewModel.dispatch(
-                        BookshelfManageScreenIntent.SetExportNoChapterName(!state.exportConfig.exportNoChapterName)
-                    )
-                }
-            )
-            RoundDropdownMenuItem(
-                text = "导出到WebDav",
-                isSelected = state.exportConfig.exportToWebDav,
-                onClick = {
-                    dismiss()
-                    viewModel.dispatch(
-                        BookshelfManageScreenIntent.SetExportToWebDav(!state.exportConfig.exportToWebDav)
-                    )
-                }
-            )
-            RoundDropdownMenuItem(
-                text = "导出插图文件",
-                isSelected = state.exportConfig.exportPictureFile,
-                onClick = {
-                    dismiss()
-                    viewModel.dispatch(
-                        BookshelfManageScreenIntent.SetExportPictureFile(!state.exportConfig.exportPictureFile)
-                    )
-                }
-            )
-            RoundDropdownMenuItem(
-                text = "并行导出",
-                isSelected = state.exportConfig.parallelExportBook,
-                onClick = {
-                    dismiss()
-                    viewModel.dispatch(
-                        BookshelfManageScreenIntent.SetParallelExportBook(!state.exportConfig.parallelExportBook)
-                    )
-                }
-            )
-            PillDivider()
             RoundDropdownMenuItem(
                 text = stringResource(R.string.log),
                 onClick = {
@@ -799,7 +683,7 @@ private fun BookshelfManageScreen(
                                         color = LegadoTheme.colorScheme.outlineVariant
                                     )
                                     SmallTonalButton(
-                                        onClick = { exportBook(book) },
+                                        onClick = { showExportSheetFor(listOf(book)) },
                                         modifier = Modifier.weight(1f),
                                         icon = Icons.Default.Upload,
                                         text = "导出",
@@ -1134,20 +1018,181 @@ private fun BookshelfManageScreen(
         onDismiss = { showDownloadAllConfirmDialog = false }
     )
 
-    OptionSheet(
-        show = showExportTypeDialog,
-        onDismissRequest = { showExportTypeDialog = false },
-        title = stringResource(R.string.export_type)
+    AppModalBottomSheet(
+        show = showExportSheet,
+        onDismissRequest = {
+            showExportSettings = false
+            showExportSheet = false
+        },
+        title = if (showExportSettings) "导出设置" else stringResource(R.string.export),
+        startAction = {
+            MediumTonalButton(
+                onClick = { showExportSettings = !showExportSettings },
+                icon = if (showExportSettings) Icons.Default.Upload else Icons.Default.Settings,
+                contentDescription = if (showExportSettings) stringResource(R.string.export) else "导出设置",
+            )
+        },
     ) {
-        exportTypes.forEachIndexed { index, type ->
-            OptionCard(
-                icon = if (type == "epub") Icons.Default.Upload else Icons.Default.Download,
-                text = type,
-                onClick = {
-                    viewModel.dispatch(BookshelfManageScreenIntent.SetExportType(index))
-                    showExportTypeDialog = false
+        AnimatedContent(
+            targetState = showExportSettings,
+            transitionSpec = {
+                fadeIn() togetherWith fadeOut() using SizeTransform(clip = false)
+            },
+            label = "ExportSheetPage",
+        ) { settings ->
+            if (settings) {
+                LazyColumn(
+                    modifier = Modifier.fillMaxWidth(),
+                    verticalArrangement = Arrangement.spacedBy(4.dp),
+                ) {
+                    item {
+                        TinyDropdownSettingItem(
+                            title = stringResource(R.string.export_type),
+                            selectedValue = state.exportConfig.exportType.toString(),
+                            displayEntries = exportTypes.toTypedArray(),
+                            entryValues = exportTypes.indices.map(Int::toString).toTypedArray(),
+                            onValueChange = { type ->
+                                viewModel.dispatch(
+                                    BookshelfManageScreenIntent.SetExportType(
+                                        type.toInt()
+                                    )
+                                )
+                            },
+                        )
+                    }
+                    item {
+                        TinyDropdownSettingItem(
+                            title = stringResource(R.string.export_charset),
+                            selectedValue = state.exportConfig.exportCharset,
+                            displayEntries = commonCharsets.toTypedArray(),
+                            entryValues = commonCharsets.toTypedArray(),
+                            onValueChange = { charset ->
+                                viewModel.dispatch(
+                                    BookshelfManageScreenIntent.SetExportCharset(charset)
+                                )
+                            },
+                        )
+                    }
+                    item {
+                        TinyClickableSettingItem(
+                            title = "自定义字符集",
+                            description = state.exportConfig.exportCharset,
+                            onClick = {
+                                exportCharsetInput =
+                                    state.exportConfig.exportCharset; showCharsetDialog = true
+                            })
+                    }
+                    item {
+                        TinyClickableSettingItem(
+                            title = stringResource(R.string.export_folder),
+                            description = ACache.get().getAsString(exportBookPathKey) ?: "未选择",
+                            onClick = { selectExportFolder() })
+                    }
+                    item {
+                        TinyClickableSettingItem(
+                            title = stringResource(R.string.export_file_name),
+                            description = state.exportConfig.bookExportFileName.orEmpty(),
+                            onClick = {
+                                exportFileNameInput =
+                                    state.exportConfig.bookExportFileName.orEmpty(); showExportFileNameDialog =
+                                true
+                            })
+                    }
+                    item {
+                        TinySwitchSettingItem(
+                            title = "替换净化",
+                            checked = state.exportConfig.exportUseReplace,
+                            onCheckedChange = {
+                                viewModel.dispatch(
+                                    BookshelfManageScreenIntent.SetExportUseReplace(it)
+                                )
+                            })
+                    }
+                    item {
+                        TinySwitchSettingItem(
+                            title = "自定义导出",
+                            checked = state.exportConfig.enableCustomExport,
+                            onCheckedChange = {
+                                viewModel.dispatch(
+                                    BookshelfManageScreenIntent.SetEnableCustomExport(it)
+                                )
+                            })
+                    }
+                    item {
+                        TinySwitchSettingItem(
+                            title = "导出包含章节名",
+                            checked = !state.exportConfig.exportNoChapterName,
+                            onCheckedChange = {
+                                viewModel.dispatch(
+                                    BookshelfManageScreenIntent.SetExportNoChapterName(!it)
+                                )
+                            })
+                    }
+                    item {
+                        TinySwitchSettingItem(
+                            title = "导出到WebDav",
+                            checked = state.exportConfig.exportToWebDav,
+                            onCheckedChange = {
+                                viewModel.dispatch(
+                                    BookshelfManageScreenIntent.SetExportToWebDav(it)
+                                )
+                            })
+                    }
+                    item {
+                        TinySwitchSettingItem(
+                            title = "导出插图文件",
+                            checked = state.exportConfig.exportPictureFile,
+                            onCheckedChange = {
+                                viewModel.dispatch(
+                                    BookshelfManageScreenIntent.SetExportPictureFile(it)
+                                )
+                            })
+                    }
+                    item {
+                        TinySwitchSettingItem(
+                            title = "并行导出",
+                            checked = state.exportConfig.parallelExportBook,
+                            onCheckedChange = {
+                                viewModel.dispatch(
+                                    BookshelfManageScreenIntent.SetParallelExportBook(it)
+                                )
+                            })
+                    }
                 }
-            )
+            } else {
+                Column(modifier = Modifier.fillMaxWidth()) {
+                    AppText(
+                        text = "本次导出(${exportSheetBooks.size} 本)",
+                        style = LegadoTheme.typography.titleSmallEmphasized,
+                        modifier = Modifier.padding(bottom = 8.dp),
+                    )
+                    LazyColumn(
+                        modifier = Modifier.weight(1f, fill = false),
+                        verticalArrangement = Arrangement.spacedBy(4.dp),
+                    ) {
+                        items(exportSheetBooks, key = { it.bookUrl }) { book ->
+                            AppText(
+                                text = book.name,
+                                maxLines = 1,
+                                overflow = TextOverflow.Ellipsis,
+                                modifier = Modifier
+                                    .fillMaxWidth()
+                                    .padding(vertical = 8.dp),
+                            )
+                        }
+                    }
+                    MediumTonalButton(
+                        onClick = {
+                            showExportSheet = false; startExportForBooks(exportSheetBooks)
+                        },
+                        modifier = Modifier
+                            .fillMaxWidth()
+                            .padding(top = 12.dp),
+                        icon = Icons.Default.Upload,
+                        text = stringResource(R.string.export),
+                    )
+                }
+            }
         }
     }