From 00dc733ec8a6d48376b239d45e22348b9b83ea7f Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Wed, 29 Apr 2026 01:42:08 +0800 Subject: [PATCH] =?UTF-8?q?[=E4=BC=98=E5=8C=96]=20=E4=B8=8B=E8=BD=BD?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ui/book/manage/BookshelfManageScreen.kt | 6 +- .../manage/BookshelfManageScreenViewModel.kt | 117 ++++++++---------- 2 files changed, 58 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt b/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt index cf9b8e64f..a06e948c5 100644 --- a/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreen.kt @@ -679,8 +679,8 @@ private fun BookshelfManageScreen( val cacheCount = remember(renderVersion, book.bookUrl) { viewModel.getCacheCount(book.bookUrl) ?: 0 } - val downloadState = state.downloadStates[book.bookUrl] - val isPreparingDownload = state.pendingDownloadBookUrls.contains(book.bookUrl) + val downloadState = viewModel.getBookDownloadState(book.bookUrl) + val isPreparingDownload = viewModel.isBookPreparingDownload(book.bookUrl) val waitingDownloadCount = downloadState?.waitingCount ?: 0 val runningDownloadCount = downloadState?.runningIndices?.size ?: 0 val isDownloadingInCacheModel = viewModel.isBookDownloading(book.bookUrl) @@ -688,7 +688,7 @@ private fun BookshelfManageScreen( waitingDownloadCount > 0 || runningDownloadCount > 0 || isDownloadingInCacheModel - val downloadFailureText = state.downloadFailureMessages[book.bookUrl]?.let { + val downloadFailureText = viewModel.getDownloadFailureMessage(book.bookUrl)?.let { stringResource(R.string.cache_download_failed, it) } val isSelected = selectedBookUrls.contains(book.bookUrl) diff --git a/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreenViewModel.kt b/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreenViewModel.kt index e66881bc9..eaae75b07 100644 --- a/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreenViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/manage/BookshelfManageScreenViewModel.kt @@ -73,9 +73,6 @@ data class BookshelfManageScreenUiState( val bookSort: Int = BookshelfConfig.bookshelfSort, val bookSortOrder: Int = BookshelfConfig.bookshelfSortOrder, val isDownloadRunning: Boolean = false, - val pendingDownloadBookUrls: Set = emptySet(), - val downloadStates: Map = emptyMap(), - val downloadFailureMessages: Map = emptyMap(), val isChangingSource: Boolean = false, val changeSourceProgress: String? = null, val changeSourceMessage: String? = null, @@ -172,6 +169,9 @@ class BookshelfManageScreenViewModel( val effects = _effects.asSharedFlow() private val cacheCounts = ConcurrentHashMap() + private val downloadStates = ConcurrentHashMap() + private val pendingDownloadBookUrls = ConcurrentHashMap.newKeySet() + private val downloadFailureMessages = ConcurrentHashMap() private var booksJob: Job? = null private var groupsJob: Job? = null private var cacheLoadJob: Job? = null @@ -309,6 +309,18 @@ class BookshelfManageScreenViewModel( return CacheBook.cacheBookMap[bookUrl]?.isStop() == false } + fun getBookDownloadState(bookUrl: String): CacheBookDownloadState? { + return downloadStates[bookUrl] + } + + fun getDownloadFailureMessage(bookUrl: String): String? { + return downloadFailureMessages[bookUrl] + } + + fun isBookPreparingDownload(bookUrl: String): Boolean { + return pendingDownloadBookUrls.contains(bookUrl) + } + private fun initialize(groupId: Long) { _uiState.update { it.copy(groupId = groupId) } syncExportConfig() @@ -385,31 +397,27 @@ class BookshelfManageScreenViewModel( } viewModelScope.launch { CacheBook.downloadStateFlow.collect { downloadState -> - _uiState.update { state -> - val successfulBookUrls = downloadState.books - .filterValues { - it.successCount > 0 && - it.failedIndices.isEmpty() && - it.failureMessage == null - } - .keys - val failureMessages = downloadState.books.mapNotNull { (bookUrl, bookState) -> - val message = bookState.failureMessage ?: if (bookState.failedIndices.isNotEmpty()) { - "${bookState.failedIndices.size} 章" - } else { - null - } - message?.let { bookUrl to it } - }.toMap() - state.copy( - isDownloadRunning = downloadState.isRunning, - pendingDownloadBookUrls = state.pendingDownloadBookUrls - downloadState.books.keys, - downloadStates = downloadState.books, - downloadFailureMessages = ( - state.downloadFailureMessages - successfulBookUrls - ) + failureMessages, - ) - } + val successfulBookUrls = downloadState.books + .filterValues { + it.successCount > 0 && + it.failedIndices.isEmpty() && + it.failureMessage == null + } + .keys + val failureMsgs = downloadState.books.mapNotNull { (bookUrl, bookState) -> + val message = bookState.failureMessage ?: if (bookState.failedIndices.isNotEmpty()) { + "${bookState.failedIndices.size} 章" + } else { + null + } + message?.let { bookUrl to it } + }.toMap() + pendingDownloadBookUrls.removeAll(downloadState.books.keys) + downloadStates.clear() + downloadStates.putAll(downloadState.books) + successfulBookUrls.forEach { downloadFailureMessages.remove(it) } + downloadFailureMessages.putAll(failureMsgs) + _uiState.update { it.copy(isDownloadRunning = downloadState.isRunning) } downloadState.books.keys.forEach { bookUrl -> scheduleDownloadStatusRefresh(bookUrl) } @@ -475,13 +483,17 @@ class BookshelfManageScreenViewModel( cacheCounts.remove(bookUrl) } } + val changedBookUrls = linkedSetOf() books.forEach { book -> if (!book.isLocal && !cacheCounts.containsKey(book.bookUrl)) { cacheCounts[book.bookUrl] = calculateCacheCount(book) - emitBookChanged(book.bookUrl) + changedBookUrls.add(book.bookUrl) } ensureActive() } + if (changedBookUrls.isNotEmpty()) { + _uiState.update { it.copy(cacheVersion = it.cacheVersion + 1) } + } } } @@ -539,9 +551,7 @@ class BookshelfManageScreenViewModel( private fun startDownloadForVisibleBooks(books: List, downloadAllChapters: Boolean) { val bookUrls = books.mapTo(hashSetOf()) { it.bookUrl } - _uiState.update { - it.copy(downloadFailureMessages = it.downloadFailureMessages - bookUrls) - } + bookUrls.forEach { downloadFailureMessages.remove(it) } execute { batchCacheDownloadUseCase.execute( bookUrls = bookUrls, @@ -555,40 +565,28 @@ class BookshelfManageScreenViewModel( private fun toggleBookDownload(book: Book) { if (book.isLocal) return - if (isBookDownloading(book.bookUrl) || uiState.value.pendingDownloadBookUrls.contains(book.bookUrl)) { + if (isBookDownloading(book.bookUrl) || pendingDownloadBookUrls.contains(book.bookUrl)) { CacheBook.remove(context, book.bookUrl) - _uiState.update { - it.copy(pendingDownloadBookUrls = it.pendingDownloadBookUrls - book.bookUrl) - } + pendingDownloadBookUrls.remove(book.bookUrl) + emitBookChanged(book.bookUrl) syncDownloadRunning() } else { - _uiState.update { - it.copy( - pendingDownloadBookUrls = it.pendingDownloadBookUrls + book.bookUrl, - downloadFailureMessages = it.downloadFailureMessages - book.bookUrl - ) - } + pendingDownloadBookUrls.add(book.bookUrl) + downloadFailureMessages.remove(book.bookUrl) + emitBookChanged(book.bookUrl) execute { cacheBookChaptersUseCase.executeRange(book.bookUrl, 0, book.lastChapterIndex) }.onSuccess { count -> if (count <= 0) { - _uiState.update { - it.copy( - pendingDownloadBookUrls = it.pendingDownloadBookUrls - book.bookUrl, - downloadFailureMessages = it.downloadFailureMessages + - (book.bookUrl to "没有可缓存的章节") - ) - } + pendingDownloadBookUrls.remove(book.bookUrl) + downloadFailureMessages[book.bookUrl] = "没有可缓存的章节" + emitBookChanged(book.bookUrl) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("没有可缓存的章节")) } }.onError { error -> - _uiState.update { - it.copy( - pendingDownloadBookUrls = it.pendingDownloadBookUrls - book.bookUrl, - downloadFailureMessages = it.downloadFailureMessages + - (book.bookUrl to (error.localizedMessage ?: "未知错误")) - ) - } + pendingDownloadBookUrls.remove(book.bookUrl) + downloadFailureMessages[book.bookUrl] = error.localizedMessage ?: "未知错误" + emitBookChanged(book.bookUrl) _effects.tryEmit(BookshelfManageScreenEffect.ShowMessage("缓存失败\n${error.localizedMessage}")) }.onFinally { syncDownloadRunning() @@ -661,9 +659,7 @@ class BookshelfManageScreenViewModel( private fun downloadBooks(bookUrls: Set, downloadAllChapters: Boolean) { if (bookUrls.isEmpty()) return - _uiState.update { - it.copy(downloadFailureMessages = it.downloadFailureMessages - bookUrls) - } + bookUrls.forEach { downloadFailureMessages.remove(it) } execute { batchCacheDownloadUseCase.execute( bookUrls = bookUrls, @@ -972,9 +968,6 @@ class BookshelfManageScreenViewModel( private fun emitBooksChanged(bookUrls: Set) { if (bookUrls.isEmpty()) return _uiState.update { it.copy(cacheVersion = it.cacheVersion + 1) } - bookUrls.forEach { bookUrl -> - _effects.tryEmit(BookshelfManageScreenEffect.NotifyBookChanged(bookUrl)) - } } }