From 5154a59e0deda70720b9fd8f22b4fa81e193dd18 Mon Sep 17 00:00:00 2001 From: HapeLee <63206378+HapeLee@users.noreply.github.com> Date: Mon, 11 May 2026 01:44:59 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=8A=A0=E8=BD=BD=E6=9B=B4?= =?UTF-8?q?=E5=A4=9A=E6=8F=90=E7=A4=BA=E6=97=A0=E9=99=90=E5=BE=AA=E7=8E=AF?= =?UTF-8?q?=E7=9A=84=E9=97=AE=E9=A2=98=20=E4=BF=AE=E5=A4=8D=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E6=96=87=E4=BB=B6JSON=E8=A7=A3=E6=9E=90NPE=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20=E4=BF=AE=E5=A4=8D=E4=B9=A6=E6=9E=B6?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=8F=8C=E9=87=8D=E8=BF=87=E6=BB=A4=E7=9A=84?= =?UTF-8?q?=E9=97=AE=E9=A2=98=20=E4=BF=AE=E5=A4=8D=E5=A4=87=E4=BB=BD?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=E9=87=8D=E5=A4=8D=E6=8F=92=E5=85=A5=E5=B4=A9?= =?UTF-8?q?=E6=BA=83=E7=9A=84=E9=97=AE=E9=A2=98=20=E4=BF=AE=E5=A4=8DRSS?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E7=8A=B6=E6=80=81=E7=BC=BA=E5=A4=B1=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../io/legado/app/help/storage/Restore.kt | 86 +++++++++++++++---- .../app/model/analyzeRule/AnalyzeUrl.kt | 4 +- .../app/ui/book/explore/ExploreShowScreen.kt | 6 +- .../ui/main/bookshelf/BookshelfViewModel.kt | 6 +- .../io/legado/app/ui/main/rss/RssViewModel.kt | 7 +- 5 files changed, 81 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/io/legado/app/help/storage/Restore.kt b/app/src/main/java/io/legado/app/help/storage/Restore.kt index 6b6f8e3d5..3bd89ec55 100644 --- a/app/src/main/java/io/legado/app/help/storage/Restore.kt +++ b/app/src/main/java/io/legado/app/help/storage/Restore.kt @@ -130,13 +130,22 @@ object Restore { appDb.bookDao.insert(*newBooks.toTypedArray()) } fileToListT(path, "bookmark.json")?.let { - appDb.bookmarkDao.insert(*it.toTypedArray()) + try { + appDb.bookmarkDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } fileToListT(path, "bookGroup.json")?.let { - appDb.bookGroupDao.insert(*it.toTypedArray()) + try { + appDb.bookGroupDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } fileToListT(path, "bookSource.json")?.let { - appDb.bookSourceDao.insert(*it.toTypedArray()) + try { + appDb.bookSourceDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } ?: run { val bookSourceFile = File(path, "bookSource.json") if (bookSourceFile.exists()) { @@ -145,54 +154,92 @@ object Restore { } } fileToListT(path, "rssSources.json")?.let { - appDb.rssSourceDao.insert(*it.toTypedArray()) + try { + appDb.rssSourceDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } fileToListT(path, "rssStar.json")?.let { - appDb.rssStarDao.insert(*it.toTypedArray()) + try { + appDb.rssStarDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } fileToListT(path, "replaceRule.json")?.let { - appDb.replaceRuleDao.insert(*it.toTypedArray()) + try { + appDb.replaceRuleDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } fileToListT(path, "searchHistory.json")?.let { - appDb.searchKeywordDao.insert(*it.toTypedArray()) + try { + appDb.searchKeywordDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } fileToListT(path, "sourceSub.json")?.let { - appDb.ruleSubDao.insert(*it.toTypedArray()) + try { + appDb.ruleSubDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } fileToListT(path, "txtTocRule.json")?.let { - appDb.txtTocRuleDao.insert(*it.toTypedArray()) + try { + appDb.txtTocRuleDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } fileToListT(path, "httpTTS.json")?.let { - appDb.httpTTSDao.insert(*it.toTypedArray()) + try { + appDb.httpTTSDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } fileToListT(path, "dictRule.json")?.let { - appDb.dictRuleDao.insert(*it.toTypedArray()) + try { + appDb.dictRuleDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } fileToListT(path, "keyboardAssists.json")?.let { - appDb.keyboardAssistsDao.insert(*it.toTypedArray()) + try { + appDb.keyboardAssistsDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } fileToListT(path, "readRecord.json")?.let { it.forEach { readRecord -> - //判断是不是本机记录 if (readRecord.deviceId != androidId) { - appDb.readRecordDao.insert(readRecord) + try { + appDb.readRecordDao.insert(readRecord) + } catch (_: SQLiteConstraintException) { + } } else { val time = appDb.readRecordDao .getReadTime(readRecord.deviceId, readRecord.bookName, readRecord.bookAuthor) if (time == null || time < readRecord.readTime) { - appDb.readRecordDao.insert(readRecord) + try { + appDb.readRecordDao.insert(readRecord) + } catch (_: SQLiteConstraintException) { + } } } } } fileToListT(path, "readRecordDetail.json")?.let { it.forEach { detail -> - appDb.readRecordDao.insertDetail(detail) + try { + appDb.readRecordDao.insertDetail(detail) + } catch (_: SQLiteConstraintException) { + } } } fileToListT(path, "readRecordSession.json")?.let { it.forEach { session -> - appDb.readRecordDao.insertSession(session) + try { + appDb.readRecordDao.insertSession(session) + } catch (_: SQLiteConstraintException) { + } } } File(path, "servers.json").takeIf { @@ -203,7 +250,10 @@ object Restore { json = aes.decryptStr(json) } GSON.fromJsonArray(json).getOrNull()?.let { - appDb.serverDao.insert(*it.toTypedArray()) + try { + appDb.serverDao.insert(*it.toTypedArray()) + } catch (_: SQLiteConstraintException) { + } } }?.onFailure { AppLog.put("恢复服务器配置出错\n${it.localizedMessage}", it) diff --git a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt index 4ea54ea7a..e6d1a5ff3 100644 --- a/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt +++ b/app/src/main/java/io/legado/app/model/analyzeRule/AnalyzeUrl.kt @@ -15,6 +15,7 @@ import io.legado.app.constant.AppConst.UA_NAME import io.legado.app.constant.AppPattern import io.legado.app.data.entities.BaseSource import io.legado.app.data.entities.Book +import io.legado.app.exception.NoStackTraceException import io.legado.app.data.entities.BookChapter import io.legado.app.help.CacheManager import io.legado.app.help.ConcurrentRateLimiter @@ -668,9 +669,10 @@ class AnalyzeUrl( * 上传文件 */ suspend fun upload(fileName: String, file: Any, contentType: String): StrResponse { + val bodyMap = GSON.fromJsonObject>(body).getOrNull() + ?: return getErrStrResponse(NoStackTraceException("请求体不是合法的JSON格式")) return getProxyClient(proxy).newCallStrResponse(retry) { url(urlNoQuery) - val bodyMap = GSON.fromJsonObject>(body).getOrNull()!! bodyMap.forEach { entry -> if (entry.value.toString() == "fileRequest") { bodyMap[entry.key] = mapOf( diff --git a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt index 06557ea0e..29081fb7b 100644 --- a/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/explore/ExploreShowScreen.kt @@ -86,7 +86,6 @@ import io.legado.app.ui.widget.components.text.AppText import io.legado.app.ui.widget.components.topbar.GlassMediumFlexibleTopAppBar import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults import io.legado.app.ui.main.bookCoverSharedElementKey -import kotlinx.coroutines.delay import org.koin.androidx.compose.koinViewModel import org.koin.compose.koinInject @@ -563,10 +562,7 @@ fun LoadMoreFooter( LaunchedEffect(isLoading, errorMsg, isEnd) { if (!isLoading && errorMsg == null && !isEnd) { - while (true) { - onRetry() - delay(1000L) - } + onRetry() } } diff --git a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt index 1cc758d49..63dc277c1 100644 --- a/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/bookshelf/BookshelfViewModel.kt @@ -397,14 +397,14 @@ class BookshelfViewModel( val uiState: StateFlow = combine( dataStateFlow, - interactionStateFlow - ) { data, interaction -> + interactionStateFlow, + visibleBooksFlow + ) { data, interaction, filteredBooks -> val books = data.books val groups = data.groups val allGroups = data.allGroups val previews = data.previews val internal = data.internal - val filteredBooks = filterBooks(books, internal.searchKey, internal.isSearchMode) val selectedGroupIndex = groups.indexOfFirst { it.groupId == internal.groupId } .coerceAtLeast(0) val currentGroupName = allGroups.firstOrNull { it.groupId == internal.groupId }?.groupName diff --git a/app/src/main/java/io/legado/app/ui/main/rss/RssViewModel.kt b/app/src/main/java/io/legado/app/ui/main/rss/RssViewModel.kt index 6739667a3..092e488e0 100644 --- a/app/src/main/java/io/legado/app/ui/main/rss/RssViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/main/rss/RssViewModel.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn @@ -35,6 +36,7 @@ class RssViewModel( val effects = _effects.asSharedFlow() init { + _uiState.update { it.copy(isLoading = true) } initGroupData() initRssData() } @@ -62,7 +64,10 @@ class RssViewModel( } .flowOn(IO) .onEach { sources -> - _uiState.update { state -> state.copy(items = sources.toImmutableList()) } + _uiState.update { state -> state.copy(items = sources.toImmutableList(), isLoading = false) } + } + .catch { + _uiState.update { it.copy(isLoading = false) } } .launchIn(viewModelScope) }