Эх сурвалжийг харах

feat: 选择模式增加删除之

HapeLee 1 сар өмнө
parent
commit
77fc2fcf16

+ 49 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfScreen.kt

@@ -56,6 +56,7 @@ import androidx.compose.material.icons.automirrored.filled.FormatListBulleted
 import androidx.compose.material.icons.filled.Bookmarks
 import androidx.compose.material.icons.filled.Check
 import androidx.compose.material.icons.filled.CloudDownload
+import androidx.compose.material.icons.filled.Delete
 import androidx.compose.material.icons.filled.Download
 import androidx.compose.material.icons.filled.Edit
 import androidx.compose.material.icons.filled.GridView
@@ -68,6 +69,7 @@ import androidx.compose.material.icons.filled.SelectAll
 import androidx.compose.material.icons.filled.UploadFile
 import androidx.compose.material.icons.filled.Wifi
 import androidx.compose.material.icons.outlined.ViewCarousel
+import androidx.compose.material3.Checkbox
 import androidx.compose.material3.ExperimentalMaterial3Api
 import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
 import androidx.compose.material3.Icon
@@ -652,6 +654,18 @@ fun BookshelfScreen(
                         )
                     }
 
+                    AnimatedVisibility(visible = isEditMode) {
+                        TopBarActionButton(
+                            onClick = {
+                                if (selectedBookUrls.isNotEmpty()) {
+                                    onIntent(BookshelfIntent.ShowOverlay(BookshelfOverlay.DeleteBooksConfirmDialog))
+                                }
+                            },
+                            imageVector = Icons.Default.Delete,
+                            contentDescription = stringResource(R.string.delete_selected)
+                        )
+                    }
+
                     if (!isEditMode) {
                         Box {
                             TopBarActionButton(
@@ -1494,6 +1508,41 @@ private fun BookshelfOverlays(
         onDismiss = { onIntent(BookshelfIntent.DismissOverlay) }
     )
 
+    if (activeOverlay == BookshelfOverlay.DeleteBooksConfirmDialog) {
+        // 勾选态只活在这次弹窗里:每次打开都从"不删源文件"起步
+        var deleteOriginal by remember { mutableStateOf(false) }
+        val hasLocalBook = remember(uiState.items, selectedBookUrls) {
+            uiState.items.any { it.book.isLocal && it.book.bookUrl in selectedBookUrls }
+        }
+        AppAlertDialog(
+            show = true,
+            onDismissRequest = { onIntent(BookshelfIntent.DismissOverlay) },
+            title = stringResource(R.string.delete_selected),
+            text = stringResource(R.string.bookshelf_selected_count, selectedBookUrls.size),
+            content = {
+                if (hasLocalBook) {
+                    Row(verticalAlignment = Alignment.CenterVertically) {
+                        Checkbox(
+                            checked = deleteOriginal,
+                            onCheckedChange = { deleteOriginal = it }
+                        )
+                        AppText(
+                            text = stringResource(R.string.delete_book_file),
+                            modifier = Modifier.padding(start = 4.dp)
+                        )
+                    }
+                }
+            },
+            confirmText = stringResource(R.string.delete),
+            onConfirm = {
+                onIntent(BookshelfIntent.DeleteBooks(selectedBookUrls, deleteOriginal))
+                onIntent(BookshelfIntent.DismissOverlay)
+            },
+            dismissText = stringResource(R.string.cancel),
+            onDismiss = { onIntent(BookshelfIntent.DismissOverlay) }
+        )
+    }
+
     if (uiState.isLoading) {
         val loadingDescription = uiState.loadingText ?: stringResource(R.string.loading)
         Dialog(onDismissRequest = {}) {

+ 4 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfUiState.kt

@@ -38,6 +38,7 @@ sealed interface BookshelfOverlay {
     data object GroupSelectSheet : BookshelfOverlay
     data class GroupEditSheet(val groupId: Long) : BookshelfOverlay
     data object BatchDownloadConfirmDialog : BookshelfOverlay
+    data object DeleteBooksConfirmDialog : BookshelfOverlay
 }
 
 sealed interface BookshelfIntent {
@@ -55,6 +56,9 @@ sealed interface BookshelfIntent {
     data class SetInFolderRoot(val value: Boolean) : BookshelfIntent
     data class MoveBooksToGroup(val bookUrls: Set<String>, val groupId: Long) : BookshelfIntent
     data class DownloadBooks(val bookUrls: Set<String>, val allChapters: Boolean = false) : BookshelfIntent
+
+    /** 编辑态批量从书架删除;[deleteOriginal] 只对本地书籍的源文件生效 */
+    data class DeleteBooks(val bookUrls: Set<String>, val deleteOriginal: Boolean) : BookshelfIntent
     data class RefreshBooks(val books: List<BookUiItem>) : BookshelfIntent
     data class StartDragging(val books: List<BookUiItem>) : BookshelfIntent
     data class MoveDragging(val from: Int, val to: Int, val books: List<BookUiItem>) : BookshelfIntent

+ 18 - 0
app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt

@@ -28,6 +28,7 @@ import io.legado.app.domain.model.isPrivateBook
 import io.legado.app.domain.model.settings.PrivateAccessSettings
 import io.legado.app.domain.usecase.AddBookUseCase
 import io.legado.app.domain.usecase.BatchCacheDownloadUseCase
+import io.legado.app.domain.usecase.DeleteBooksUseCase
 import io.legado.app.domain.usecase.ExportBookshelfUseCase
 import io.legado.app.domain.usecase.ImportBookshelfUseCase
 import io.legado.app.domain.usecase.RefreshTocUseCase
@@ -93,6 +94,7 @@ class BookshelfViewModel(
     private val updateBooksGroupUseCase: UpdateBooksGroupUseCase,
     private val refreshTocUseCase: RefreshTocUseCase,
     private val addBookUseCase: AddBookUseCase,
+    private val deleteBooksUseCase: DeleteBooksUseCase,
     private val importBookshelfUseCase: ImportBookshelfUseCase,
     private val exportBookshelfUseCase: ExportBookshelfUseCase,
     private val bookshelfSettingsGateway: BookshelfSettingsGateway,
@@ -804,6 +806,7 @@ class BookshelfViewModel(
             is BookshelfIntent.SetInFolderRoot -> setInFolderRoot(intent.value)
             is BookshelfIntent.MoveBooksToGroup -> moveBooksToGroup(intent.bookUrls, intent.groupId)
             is BookshelfIntent.DownloadBooks -> downloadBooks(intent.bookUrls, intent.allChapters)
+            is BookshelfIntent.DeleteBooks -> deleteBooks(intent.bookUrls, intent.deleteOriginal)
             is BookshelfIntent.RefreshBooks -> refreshBooks(intent.books)
             is BookshelfIntent.StartDragging -> startDraggingBooks(intent.books)
             is BookshelfIntent.MoveDragging -> moveDraggingBook(intent.from, intent.to, intent.books)
@@ -1090,6 +1093,21 @@ class BookshelfViewModel(
         }
     }
 
+    fun deleteBooks(bookUrls: Set<String>, deleteOriginal: Boolean) {
+        if (bookUrls.isEmpty()) return
+        execute {
+            deleteBooksUseCase.execute(bookUrls, deleteOriginal)
+        }.onSuccess { deletedBookUrls ->
+            // 已经删掉的书不能继续留在选中集合里,否则下一次批量操作会带上幽灵 url
+            val remaining = selectedBookUrlsFlow.value - deletedBookUrls.toSet()
+            if (remaining.size != selectedBookUrlsFlow.value.size) {
+                selectedBookUrlsFlow.value = remaining
+            }
+        }.onError {
+            showMessage(context.getString(R.string.delete_failed) + "\n" + it.localizedMessage)
+        }
+    }
+
     fun refreshBooks(books: List<BookUiItem>) {
         if (isRefreshingFlow.value) return
         isRefreshingFlow.value = true