From 5ddaa70f67bf39f9d52411cb0cd7ca5f68eb7df7 Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Thu, 30 Apr 2026 13:07:58 +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 --- .../io/legado/app/model/CacheBookModel.kt | 39 ++++++++++++++++++- .../model/cache/CacheDownloadRepository.kt | 2 +- .../app/ui/book/search/SearchViewModel.kt | 21 +++++++--- 3 files changed, 55 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/io/legado/app/model/CacheBookModel.kt b/app/src/main/java/io/legado/app/model/CacheBookModel.kt index 3ad28263c..d0d5e3ceb 100644 --- a/app/src/main/java/io/legado/app/model/CacheBookModel.kt +++ b/app/src/main/java/io/legado/app/model/CacheBookModel.kt @@ -55,6 +55,7 @@ class CacheBookModel( private val onDownloadSet = linkedSetOf() private val canceledDownloadSet = hashSetOf() private val pausedChapterSet = linkedSetOf() + private val pendingReadRequestMap = hashMapOf() private val chapterTasks = hashMapOf>() private val tasks = CompositeCoroutine() private val repository = CacheDownloadRepository() @@ -197,6 +198,7 @@ class CacheBookModel( canceledDownloadSet.clear() pausedChapterSet.clear() chapterTasks.clear() + pendingReadRequestMap.clear() tasks.clear() retryCountMap.clear() isStopped = true @@ -492,12 +494,17 @@ class CacheBookModel( ) { task.onSuccess { onSuccess(chapter) + (it as? String)?.let { content -> + emitPendingReadContent(chapter, content) + } }.onError { onPreError(chapter, it) delay(1000) onPostError(chapter, it) + emitPendingReadError(chapter, it) }.onCancel { onCancel(chapterIndex) + emitPendingReadCanceled(chapter) }.onFinally { chapterTasks.remove(chapterIndex)?.let { tasks.delete(it) } onFinally() @@ -536,7 +543,10 @@ class CacheBookModel( semaphore: Semaphore?, resetPageOffset: Boolean = false ) { - if (!markChapterDownloadStarted(chapter.index)) return + if (!markChapterDownloadStarted(chapter.index)) { + markPendingReadRequest(chapter.index, resetPageOffset) + return + } repository.downloadContentTask( scope = scope, bookSource = bookSource, @@ -551,11 +561,13 @@ class CacheBookModel( ReadBook.downloadedChapters.add(chapter.index) ReadBook.downloadFailChapters.remove(chapter.index) downloadFinish(chapter, content, resetPageOffset) + emitPendingReadContent(chapter, content) }.onError { onError(chapter, it) ReadBook.downloadFailChapters[chapter.index] = (ReadBook.downloadFailChapters[chapter.index] ?: 0) + 1 downloadFinish(chapter, "获取正文失败\n${it.localizedMessage}", resetPageOffset) + emitPendingReadError(chapter, it) }.onCancel { onCancel(chapter.index, requeue = false) downloadFinish(chapter, "download canceled", resetPageOffset, true) @@ -573,6 +585,31 @@ class CacheBookModel( return true } + @Synchronized + private fun markPendingReadRequest(index: Int, resetPageOffset: Boolean) { + pendingReadRequestMap[index] = pendingReadRequestMap[index] == true || resetPageOffset + } + + @Synchronized + private fun consumePendingReadRequest(index: Int): Boolean? { + return pendingReadRequestMap.remove(index) + } + + private fun emitPendingReadContent(chapter: BookChapter, content: String) { + val resetPageOffset = consumePendingReadRequest(chapter.index) ?: return + downloadFinish(chapter, content, resetPageOffset) + } + + private fun emitPendingReadError(chapter: BookChapter, error: Throwable) { + val resetPageOffset = consumePendingReadRequest(chapter.index) ?: return + downloadFinish(chapter, "获取正文失败\n${error.localizedMessage}", resetPageOffset) + } + + private fun emitPendingReadCanceled(chapter: BookChapter) { + val resetPageOffset = consumePendingReadRequest(chapter.index) ?: return + downloadFinish(chapter, "download canceled", resetPageOffset) + } + private fun downloadFinish( chapter: BookChapter, content: String, diff --git a/app/src/main/java/io/legado/app/model/cache/CacheDownloadRepository.kt b/app/src/main/java/io/legado/app/model/cache/CacheDownloadRepository.kt index 776b40612..99ac23d30 100644 --- a/app/src/main/java/io/legado/app/model/cache/CacheDownloadRepository.kt +++ b/app/src/main/java/io/legado/app/model/cache/CacheDownloadRepository.kt @@ -71,7 +71,7 @@ class CacheDownloadRepository { context: CoroutineContext, start: CoroutineStart = CoroutineStart.LAZY, executeContext: CoroutineContext = context, - ): Coroutine { + ): Coroutine { return Coroutine.async( scope = scope, context = context, diff --git a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt index f1d31ceb6..58b598ab5 100644 --- a/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/search/SearchViewModel.kt @@ -126,15 +126,17 @@ class SearchViewModel( } SearchIntent.SelectAllScope -> { + val oldScope = searchScope.toString() searchScope.update("") - syncScopeState() + syncScopeState(restartSearch = true, oldScope = oldScope) } is SearchIntent.ToggleScopeGroup -> toggleScopeGroup(intent.groupName) is SearchIntent.ToggleScopeSource -> toggleScopeSource(intent.source) is SearchIntent.RemoveScopeItem -> { + val oldScope = searchScope.toString() searchScope.remove(intent.scopeName) - syncScopeState() + syncScopeState(restartSearch = true, oldScope = oldScope) } is SearchIntent.TogglePrecision -> { @@ -370,6 +372,7 @@ class SearchViewModel( } private fun toggleScopeGroup(groupName: String) { + val oldScope = searchScope.toString() if (searchScope.isSource()) { searchScope.update("") } @@ -380,10 +383,11 @@ class SearchViewModel( selected.add(groupName) } searchScope.update(selected.toList()) - syncScopeState() + syncScopeState(restartSearch = true, oldScope = oldScope) } private fun toggleScopeSource(source: BookSourcePart) { + val oldScope = searchScope.toString() val selectedUrls = if (searchScope.isSource()) { searchScope.sourceUrls.toMutableSet() } else { @@ -404,7 +408,7 @@ class SearchViewModel( } searchScope.updateSources(selectedSources) } - syncScopeState() + syncScopeState(restartSearch = true, oldScope = oldScope) } private fun handleEmptyScopeActionConfirmed() { @@ -429,7 +433,11 @@ class SearchViewModel( } } - private fun syncScopeState() { + private fun syncScopeState( + restartSearch: Boolean = false, + oldScope: String? = null, + ) { + val scopeChanged = oldScope == null || oldScope != searchScope.toString() _uiState.update { it.copy( scopeDisplay = searchScope.display, @@ -439,6 +447,9 @@ class SearchViewModel( isSourceScope = searchScope.isSource(), ) } + if (restartSearch && scopeChanged) { + restartCommittedSearchIfNeeded() + } } private fun List.toSearchResultItems(