|
@@ -130,6 +130,7 @@ class BookCacheManageViewModel(
|
|
|
private var observeJob: Job? = null
|
|
private var observeJob: Job? = null
|
|
|
private var fullReloadJob: Job? = null
|
|
private var fullReloadJob: Job? = null
|
|
|
private val bookReloadJobs = hashMapOf<String, Job>()
|
|
private val bookReloadJobs = hashMapOf<String, Job>()
|
|
|
|
|
+ private val chapterLoadJobs = hashMapOf<String, Job>()
|
|
|
private val pendingDownloadRefreshBookUrls = ConcurrentHashMap.newKeySet<String>()
|
|
private val pendingDownloadRefreshBookUrls = ConcurrentHashMap.newKeySet<String>()
|
|
|
@Volatile
|
|
@Volatile
|
|
|
private var pendingDownloadSummaryRefresh = false
|
|
private var pendingDownloadSummaryRefresh = false
|
|
@@ -179,16 +180,31 @@ class BookCacheManageViewModel(
|
|
|
}
|
|
}
|
|
|
viewModelScope.launch {
|
|
viewModelScope.launch {
|
|
|
CacheBook.cacheSuccessFlow.collect { chapter ->
|
|
CacheBook.cacheSuccessFlow.collect { chapter ->
|
|
|
- scheduleDownloadStatusRefresh(chapter.bookUrl)
|
|
|
|
|
|
|
+ // 增量标记已缓存,避免每成功一章就 countImageCachedChapters / 展开列表扫盘
|
|
|
|
|
+ markChapterCachedInUi(chapter.bookUrl, chapter.index)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
viewModelScope.launch {
|
|
viewModelScope.launch {
|
|
|
|
|
+ var previousBooks = emptyMap<String, CacheBookDownloadState>()
|
|
|
CacheBook.downloadStateFlow.collect { state ->
|
|
CacheBook.downloadStateFlow.collect { state ->
|
|
|
|
|
+ val prev = previousBooks
|
|
|
|
|
+ previousBooks = state.books
|
|
|
state.books.forEach { (bookUrl, bookState) ->
|
|
state.books.forEach { (bookUrl, bookState) ->
|
|
|
- if (bookState.runningIndices.isNotEmpty() || bookState.chapterProgress.isNotEmpty()) {
|
|
|
|
|
|
|
+ val old = prev[bookUrl]
|
|
|
|
|
+ if (old == bookState) return@forEach
|
|
|
|
|
+ val hasActivity = bookState.runningIndices.isNotEmpty() ||
|
|
|
|
|
+ bookState.chapterProgress.isNotEmpty()
|
|
|
|
|
+ if (hasActivity) {
|
|
|
scheduleProgressUpdate(bookUrl, debounceMillis = 100)
|
|
scheduleProgressUpdate(bookUrl, debounceMillis = 100)
|
|
|
} else {
|
|
} else {
|
|
|
- scheduleDownloadStatusRefresh(bookUrl)
|
|
|
|
|
|
|
+ // 排队/暂停/结束:只刷内存运行时态;isCached 由 cacheSuccessFlow 增量校正
|
|
|
|
|
+ scheduleRuntimeStatusRefresh(bookUrl)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 从 state 移除的书也要刷掉运行时角标
|
|
|
|
|
+ prev.keys.forEach { bookUrl ->
|
|
|
|
|
+ if (!state.books.containsKey(bookUrl)) {
|
|
|
|
|
+ scheduleRuntimeStatusRefresh(bookUrl)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
pendingDownloadSummaryRefresh = true
|
|
pendingDownloadSummaryRefresh = true
|
|
@@ -196,13 +212,13 @@ class BookCacheManageViewModel(
|
|
|
}
|
|
}
|
|
|
viewModelScope.launch {
|
|
viewModelScope.launch {
|
|
|
CacheBook.pendingAdmissionFlow.collect { pending ->
|
|
CacheBook.pendingAdmissionFlow.collect { pending ->
|
|
|
- pending.keys.forEach { scheduleDownloadStatusRefresh(it) }
|
|
|
|
|
|
|
+ pending.keys.forEach { scheduleRuntimeStatusRefresh(it) }
|
|
|
pendingDownloadSummaryRefresh = true
|
|
pendingDownloadSummaryRefresh = true
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
viewModelScope.launch {
|
|
viewModelScope.launch {
|
|
|
CacheBook.queueChangedFlow.collect { bookUrl ->
|
|
CacheBook.queueChangedFlow.collect { bookUrl ->
|
|
|
- scheduleDownloadStatusRefresh(bookUrl)
|
|
|
|
|
|
|
+ scheduleRuntimeStatusRefresh(bookUrl)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
viewModelScope.launch {
|
|
viewModelScope.launch {
|
|
@@ -258,7 +274,7 @@ class BookCacheManageViewModel(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private fun scheduleDownloadStatusRefresh(bookUrl: String) {
|
|
|
|
|
|
|
+ private fun scheduleRuntimeStatusRefresh(bookUrl: String) {
|
|
|
if (bookUrl.isNotBlank()) {
|
|
if (bookUrl.isNotBlank()) {
|
|
|
pendingDownloadRefreshBookUrls.add(bookUrl)
|
|
pendingDownloadRefreshBookUrls.add(bookUrl)
|
|
|
}
|
|
}
|
|
@@ -270,8 +286,10 @@ class BookCacheManageViewModel(
|
|
|
bookUrls.forEach { pendingDownloadRefreshBookUrls.remove(it) }
|
|
bookUrls.forEach { pendingDownloadRefreshBookUrls.remove(it) }
|
|
|
val shouldRefreshSummary = pendingDownloadSummaryRefresh || bookUrls.isNotEmpty()
|
|
val shouldRefreshSummary = pendingDownloadSummaryRefresh || bookUrls.isNotEmpty()
|
|
|
pendingDownloadSummaryRefresh = false
|
|
pendingDownloadSummaryRefresh = false
|
|
|
- bookUrls.forEach { bookUrl ->
|
|
|
|
|
- reloadBook(bookUrl)
|
|
|
|
|
|
|
+ if (bookUrls.isNotEmpty()) {
|
|
|
|
|
+ // 批量轻量刷新:只改内存中的排队/进度态,并对列表按 FIFO 重排
|
|
|
|
|
+ updateRuntimeDownloadStatuses(bookUrls)
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
if (shouldRefreshSummary) {
|
|
if (shouldRefreshSummary) {
|
|
|
_uiState.update {
|
|
_uiState.update {
|
|
@@ -291,13 +309,74 @@ class BookCacheManageViewModel(
|
|
|
if (debounceMillis > 0) {
|
|
if (debounceMillis > 0) {
|
|
|
delay(debounceMillis)
|
|
delay(debounceMillis)
|
|
|
}
|
|
}
|
|
|
- updateDownloadProgressOnly(bookUrl)
|
|
|
|
|
|
|
+ // 进度刷新不重排列表,避免下载中 LazyColumn 频繁挪动项导致折叠/点击卡顿
|
|
|
|
|
+ updateRuntimeDownloadStatuses(listOf(bookUrl), resort = false)
|
|
|
if (bookReloadJobs[bookUrl] == currentCoroutineContext()[Job]) {
|
|
if (bookReloadJobs[bookUrl] == currentCoroutineContext()[Job]) {
|
|
|
bookReloadJobs.remove(bookUrl)
|
|
bookReloadJobs.remove(bookUrl)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /** 仅刷新运行时下载态(等待/下载中/暂停/进度),不扫磁盘、不重建章节缓存标记。 */
|
|
|
|
|
+ private fun scheduleImmediateRuntimeStatusRefresh(bookUrl: String) {
|
|
|
|
|
+ viewModelScope.launch {
|
|
|
|
|
+ updateRuntimeDownloadStatuses(listOf(bookUrl), resort = true)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun refreshAllRuntimeDownloadStatuses() {
|
|
|
|
|
+ val bookUrls = (uiState.value.shelfBooks + uiState.value.notShelfBooks).map { it.bookUrl }
|
|
|
|
|
+ updateRuntimeDownloadStatuses(bookUrls, resort = true)
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private fun markChapterCachedInUi(bookUrl: String, chapterIndex: Int) {
|
|
|
|
|
+ if (bookUrl.isBlank()) return
|
|
|
|
|
+ _uiState.update { state ->
|
|
|
|
|
+ val chapters = state.chaptersByBookUrl[bookUrl]?.map { chapter ->
|
|
|
|
|
+ if (chapter.index != chapterIndex) {
|
|
|
|
|
+ chapter
|
|
|
|
|
+ } else {
|
|
|
|
|
+ chapter.copy(
|
|
|
|
|
+ isCached = true,
|
|
|
|
|
+ isWaiting = false,
|
|
|
|
|
+ isDownloading = false,
|
|
|
|
|
+ isPaused = false,
|
|
|
|
|
+ isError = false,
|
|
|
|
|
+ downloadProgress = null,
|
|
|
|
|
+ progressLabel = null,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ val sortedBooks = sortItems(
|
|
|
|
|
+ (state.shelfBooks + state.notShelfBooks).map { item ->
|
|
|
|
|
+ if (item.bookUrl != bookUrl) {
|
|
|
|
|
+ item
|
|
|
|
|
+ } else {
|
|
|
|
|
+ val bookState = CacheBook.downloadStateFlow.value.books[bookUrl]
|
|
|
|
|
+ ?: CacheBookDownloadState(bookUrl)
|
|
|
|
|
+ applyDownloadStateToBookItem(
|
|
|
|
|
+ item.copy(cachedCount = min(item.cachedCount + 1, item.totalCount)),
|
|
|
|
|
+ bookState,
|
|
|
|
|
+ CacheBook.cacheBookMap[bookUrl],
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ )
|
|
|
|
|
+ state.copy(
|
|
|
|
|
+ shelfBooks = sortedBooks.filterNot { it.isNotShelf },
|
|
|
|
|
+ notShelfBooks = sortedBooks.filter { it.isNotShelf },
|
|
|
|
|
+ chaptersByBookUrl = if (chapters != null) {
|
|
|
|
|
+ state.chaptersByBookUrl + (bookUrl to chapters)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ state.chaptersByBookUrl
|
|
|
|
|
+ },
|
|
|
|
|
+ downloadSummary = buildDownloadSummary(sortedBooks),
|
|
|
|
|
+ hasPausedDownloads = CacheBook.hasPausedDownloads,
|
|
|
|
|
+ version = state.version + 1,
|
|
|
|
|
+ )
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private fun scheduleBookReload(bookUrl: String, debounceMillis: Long = 80) {
|
|
private fun scheduleBookReload(bookUrl: String, debounceMillis: Long = 80) {
|
|
|
if (bookUrl.isBlank()) return
|
|
if (bookUrl.isBlank()) return
|
|
|
bookReloadJobs.remove(bookUrl)?.cancel()
|
|
bookReloadJobs.remove(bookUrl)?.cancel()
|
|
@@ -481,27 +560,31 @@ class BookCacheManageViewModel(
|
|
|
|
|
|
|
|
private fun toggleBookExpanded(bookUrl: String) {
|
|
private fun toggleBookExpanded(bookUrl: String) {
|
|
|
val shouldExpand = !_uiState.value.expandedBookUrls.contains(bookUrl)
|
|
val shouldExpand = !_uiState.value.expandedBookUrls.contains(bookUrl)
|
|
|
- _uiState.update { state ->
|
|
|
|
|
- if (shouldExpand) {
|
|
|
|
|
- state.copy(
|
|
|
|
|
- expandedBookUrls = state.expandedBookUrls + bookUrl,
|
|
|
|
|
- version = state.version + 1,
|
|
|
|
|
- )
|
|
|
|
|
- } else {
|
|
|
|
|
|
|
+ if (!shouldExpand) {
|
|
|
|
|
+ // 折叠立即生效,并取消进行中的章节列表构建,避免扫盘结果回写抢主线程
|
|
|
|
|
+ bookReloadJobs.remove(bookUrl)?.cancel()
|
|
|
|
|
+ chapterLoadJobs.remove(bookUrl)?.cancel()
|
|
|
|
|
+ _uiState.update { state ->
|
|
|
state.copy(
|
|
state.copy(
|
|
|
expandedBookUrls = state.expandedBookUrls - bookUrl,
|
|
expandedBookUrls = state.expandedBookUrls - bookUrl,
|
|
|
chaptersByBookUrl = state.chaptersByBookUrl - bookUrl,
|
|
chaptersByBookUrl = state.chaptersByBookUrl - bookUrl,
|
|
|
version = state.version + 1,
|
|
version = state.version + 1,
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
|
|
+ return
|
|
|
}
|
|
}
|
|
|
- if (shouldExpand) {
|
|
|
|
|
- loadBookChapters(bookUrl)
|
|
|
|
|
|
|
+ _uiState.update { state ->
|
|
|
|
|
+ state.copy(
|
|
|
|
|
+ expandedBookUrls = state.expandedBookUrls + bookUrl,
|
|
|
|
|
+ version = state.version + 1,
|
|
|
|
|
+ )
|
|
|
}
|
|
}
|
|
|
|
|
+ loadBookChapters(bookUrl)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private fun loadBookChapters(bookUrl: String) {
|
|
private fun loadBookChapters(bookUrl: String) {
|
|
|
- viewModelScope.launch {
|
|
|
|
|
|
|
+ chapterLoadJobs.remove(bookUrl)?.cancel()
|
|
|
|
|
+ chapterLoadJobs[bookUrl] = viewModelScope.launch {
|
|
|
val chapters = withContext(Dispatchers.IO) {
|
|
val chapters = withContext(Dispatchers.IO) {
|
|
|
buildChapterItems(bookUrl)
|
|
buildChapterItems(bookUrl)
|
|
|
}
|
|
}
|
|
@@ -515,6 +598,9 @@ class BookCacheManageViewModel(
|
|
|
)
|
|
)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+ if (chapterLoadJobs[bookUrl] == currentCoroutineContext()[Job]) {
|
|
|
|
|
+ chapterLoadJobs.remove(bookUrl)
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -522,7 +608,8 @@ class BookCacheManageViewModel(
|
|
|
execute {
|
|
execute {
|
|
|
CacheBook.pause(context)
|
|
CacheBook.pause(context)
|
|
|
}.onFinally {
|
|
}.onFinally {
|
|
|
- reloadAll(forceDatabase = true)
|
|
|
|
|
|
|
+ // 勿 forceDatabase 全量重载:展开章列表扫盘会卡住折叠/启停
|
|
|
|
|
+ refreshAllRuntimeDownloadStatuses()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -530,13 +617,14 @@ class BookCacheManageViewModel(
|
|
|
execute {
|
|
execute {
|
|
|
CacheBook.pauseBook(context, bookUrl)
|
|
CacheBook.pauseBook(context, bookUrl)
|
|
|
}.onFinally {
|
|
}.onFinally {
|
|
|
- scheduleBookReload(bookUrl, debounceMillis = 0)
|
|
|
|
|
|
|
+ scheduleImmediateRuntimeStatusRefresh(bookUrl)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private fun startAllDownloads() {
|
|
private fun startAllDownloads() {
|
|
|
val items = uiState.value.shelfBooks + uiState.value.notShelfBooks
|
|
val items = uiState.value.shelfBooks + uiState.value.notShelfBooks
|
|
|
execute {
|
|
execute {
|
|
|
|
|
+ // FAB 一键开始:先全局解冻所有已在任务里的书/章;无任务时再为全部缺章书入队
|
|
|
if (CacheBook.resume(context)) {
|
|
if (CacheBook.resume(context)) {
|
|
|
return@execute null
|
|
return@execute null
|
|
|
}
|
|
}
|
|
@@ -558,7 +646,7 @@ class BookCacheManageViewModel(
|
|
|
}.onError {
|
|
}.onError {
|
|
|
_effects.tryEmit(BookCacheManageEffect.ShowMessage("加入缓存队列失败\n${it.localizedMessage}"))
|
|
_effects.tryEmit(BookCacheManageEffect.ShowMessage("加入缓存队列失败\n${it.localizedMessage}"))
|
|
|
}.onFinally {
|
|
}.onFinally {
|
|
|
- reloadAll(forceDatabase = true)
|
|
|
|
|
|
|
+ refreshAllRuntimeDownloadStatuses()
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -582,7 +670,7 @@ class BookCacheManageViewModel(
|
|
|
}.onError {
|
|
}.onError {
|
|
|
_effects.tryEmit(BookCacheManageEffect.ShowMessage("加入缓存队列失败\n${it.localizedMessage}"))
|
|
_effects.tryEmit(BookCacheManageEffect.ShowMessage("加入缓存队列失败\n${it.localizedMessage}"))
|
|
|
}.onFinally {
|
|
}.onFinally {
|
|
|
- scheduleBookReload(bookUrl, debounceMillis = 0)
|
|
|
|
|
|
|
+ scheduleImmediateRuntimeStatusRefresh(bookUrl)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -635,42 +723,57 @@ class BookCacheManageViewModel(
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- private fun updateDownloadProgressOnly(bookUrl: String) {
|
|
|
|
|
- val bookState = CacheBook.downloadStateFlow.value.books[bookUrl] ?: return
|
|
|
|
|
- val model = CacheBook.cacheBookMap[bookUrl]
|
|
|
|
|
|
|
+ private fun updateRuntimeDownloadStatuses(
|
|
|
|
|
+ bookUrls: Collection<String>,
|
|
|
|
|
+ resort: Boolean = true,
|
|
|
|
|
+ ) {
|
|
|
|
|
+ if (bookUrls.isEmpty()) return
|
|
|
|
|
+ val targets = bookUrls.filter { it.isNotBlank() }.toSet()
|
|
|
|
|
+ if (targets.isEmpty()) return
|
|
|
|
|
+ val stateSnapshot = CacheBook.downloadStateFlow.value.books
|
|
|
_uiState.update { state ->
|
|
_uiState.update { state ->
|
|
|
fun updateBookItem(item: BookCacheBookItem): BookCacheBookItem {
|
|
fun updateBookItem(item: BookCacheBookItem): BookCacheBookItem {
|
|
|
- if (item.bookUrl != bookUrl) return item
|
|
|
|
|
|
|
+ if (item.bookUrl !in targets) return item
|
|
|
|
|
+ val bookState = stateSnapshot[item.bookUrl] ?: CacheBookDownloadState(item.bookUrl)
|
|
|
|
|
+ val model = CacheBook.cacheBookMap[item.bookUrl]
|
|
|
return applyDownloadStateToBookItem(item, bookState, model)
|
|
return applyDownloadStateToBookItem(item, bookState, model)
|
|
|
}
|
|
}
|
|
|
- val updatedChapters = state.chaptersByBookUrl[bookUrl]?.map { item ->
|
|
|
|
|
- val isPaused = model?.isPaused(item.index) == true
|
|
|
|
|
- val isWaiting = !isPaused && model?.isWaiting(item.index) == true
|
|
|
|
|
- val isDownloading = !isPaused && model?.isDownloading(item.index) == true
|
|
|
|
|
- val chapterProgress = bookState.chapterProgress[item.index]
|
|
|
|
|
- val (downloadProgress, progressLabel) = chapterProgressUi(
|
|
|
|
|
- isDownloading = isDownloading,
|
|
|
|
|
- progress = chapterProgress,
|
|
|
|
|
- )
|
|
|
|
|
- item.copy(
|
|
|
|
|
- isWaiting = isWaiting,
|
|
|
|
|
- isDownloading = isDownloading,
|
|
|
|
|
- isPaused = isPaused,
|
|
|
|
|
- downloadProgress = downloadProgress,
|
|
|
|
|
- progressLabel = progressLabel,
|
|
|
|
|
- )
|
|
|
|
|
|
|
+
|
|
|
|
|
+ var chaptersByBookUrl = state.chaptersByBookUrl
|
|
|
|
|
+ targets.forEach { bookUrl ->
|
|
|
|
|
+ val existing = chaptersByBookUrl[bookUrl] ?: return@forEach
|
|
|
|
|
+ val bookState = stateSnapshot[bookUrl] ?: CacheBookDownloadState(bookUrl)
|
|
|
|
|
+ val model = CacheBook.cacheBookMap[bookUrl]
|
|
|
|
|
+ val errors = errorIndices(bookUrl)
|
|
|
|
|
+ chaptersByBookUrl = chaptersByBookUrl + (bookUrl to existing.map { item ->
|
|
|
|
|
+ val isPaused = model?.isPaused(item.index) == true
|
|
|
|
|
+ val isWaiting = !isPaused && model?.isWaiting(item.index) == true
|
|
|
|
|
+ val isDownloading = !isPaused && model?.isDownloading(item.index) == true
|
|
|
|
|
+ val chapterProgress = bookState.chapterProgress[item.index]
|
|
|
|
|
+ val (downloadProgress, progressLabel) = chapterProgressUi(
|
|
|
|
|
+ isDownloading = isDownloading,
|
|
|
|
|
+ progress = chapterProgress,
|
|
|
|
|
+ )
|
|
|
|
|
+ item.copy(
|
|
|
|
|
+ // 进行中强制未缓存;完成后由 markChapterCachedInUi 校正 isCached/cachedCount
|
|
|
|
|
+ isCached = if (isDownloading || isWaiting || isPaused) false else item.isCached,
|
|
|
|
|
+ isWaiting = isWaiting,
|
|
|
|
|
+ isDownloading = isDownloading,
|
|
|
|
|
+ isPaused = isPaused,
|
|
|
|
|
+ isError = errors.contains(item.index),
|
|
|
|
|
+ downloadProgress = downloadProgress,
|
|
|
|
|
+ progressLabel = progressLabel,
|
|
|
|
|
+ )
|
|
|
|
|
+ })
|
|
|
}
|
|
}
|
|
|
- val shelfBooks = state.shelfBooks.map(::updateBookItem)
|
|
|
|
|
- val notShelfBooks = state.notShelfBooks.map(::updateBookItem)
|
|
|
|
|
|
|
+
|
|
|
|
|
+ val updatedBooks = (state.shelfBooks + state.notShelfBooks).map(::updateBookItem)
|
|
|
|
|
+ val finalBooks = if (resort) sortItems(updatedBooks) else updatedBooks
|
|
|
state.copy(
|
|
state.copy(
|
|
|
- shelfBooks = shelfBooks,
|
|
|
|
|
- notShelfBooks = notShelfBooks,
|
|
|
|
|
- chaptersByBookUrl = if (updatedChapters != null) {
|
|
|
|
|
- state.chaptersByBookUrl + (bookUrl to updatedChapters)
|
|
|
|
|
- } else {
|
|
|
|
|
- state.chaptersByBookUrl
|
|
|
|
|
- },
|
|
|
|
|
- downloadSummary = buildDownloadSummary(shelfBooks + notShelfBooks),
|
|
|
|
|
|
|
+ shelfBooks = finalBooks.filterNot { item -> item.isNotShelf },
|
|
|
|
|
+ notShelfBooks = finalBooks.filter { item -> item.isNotShelf },
|
|
|
|
|
+ chaptersByBookUrl = chaptersByBookUrl,
|
|
|
|
|
+ downloadSummary = buildDownloadSummary(finalBooks),
|
|
|
hasPausedDownloads = CacheBook.hasPausedDownloads,
|
|
hasPausedDownloads = CacheBook.hasPausedDownloads,
|
|
|
version = state.version + 1,
|
|
version = state.version + 1,
|
|
|
)
|
|
)
|
|
@@ -728,7 +831,7 @@ class BookCacheManageViewModel(
|
|
|
}.onError {
|
|
}.onError {
|
|
|
_effects.tryEmit(BookCacheManageEffect.ShowMessage("章节缓存失败\n${it.localizedMessage}"))
|
|
_effects.tryEmit(BookCacheManageEffect.ShowMessage("章节缓存失败\n${it.localizedMessage}"))
|
|
|
}.onFinally {
|
|
}.onFinally {
|
|
|
- scheduleBookReload(bookUrl, debounceMillis = 0)
|
|
|
|
|
|
|
+ scheduleImmediateRuntimeStatusRefresh(bookUrl)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -736,7 +839,7 @@ class BookCacheManageViewModel(
|
|
|
execute {
|
|
execute {
|
|
|
CacheBook.pauseChapter(bookUrl, chapterIndex)
|
|
CacheBook.pauseChapter(bookUrl, chapterIndex)
|
|
|
}.onSuccess {
|
|
}.onSuccess {
|
|
|
- scheduleBookReload(bookUrl, debounceMillis = 0)
|
|
|
|
|
|
|
+ scheduleImmediateRuntimeStatusRefresh(bookUrl)
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -767,9 +870,7 @@ class BookCacheManageViewModel(
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private fun sortItems(items: List<BookCacheBookItem>): List<BookCacheBookItem> {
|
|
private fun sortItems(items: List<BookCacheBookItem>): List<BookCacheBookItem> {
|
|
|
- return items.sortedWith(compareByDescending<BookCacheBookItem> { it.isDownloading }
|
|
|
|
|
- .thenByDescending { it.cachedCount }
|
|
|
|
|
- .thenBy { it.name })
|
|
|
|
|
|
|
+ return sortBookCacheItems(items, CacheBook.explicitDownloadOrder())
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
private fun buildDownloadSummary(items: List<BookCacheBookItem>): String {
|
|
private fun buildDownloadSummary(items: List<BookCacheBookItem>): String {
|