修复加载更多提示无限循环的问题
修复上传文件JSON解析NPE的问题 修复书架数据双重过滤的问题 修复备份恢复重复插入崩溃的问题 修复RSS加载状态缺失问题
This commit is contained in:
@@ -130,13 +130,22 @@ object Restore {
|
|||||||
appDb.bookDao.insert(*newBooks.toTypedArray())
|
appDb.bookDao.insert(*newBooks.toTypedArray())
|
||||||
}
|
}
|
||||||
fileToListT<Bookmark>(path, "bookmark.json")?.let {
|
fileToListT<Bookmark>(path, "bookmark.json")?.let {
|
||||||
appDb.bookmarkDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.bookmarkDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileToListT<BookGroup>(path, "bookGroup.json")?.let {
|
fileToListT<BookGroup>(path, "bookGroup.json")?.let {
|
||||||
appDb.bookGroupDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.bookGroupDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileToListT<BookSource>(path, "bookSource.json")?.let {
|
fileToListT<BookSource>(path, "bookSource.json")?.let {
|
||||||
appDb.bookSourceDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.bookSourceDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
} ?: run {
|
} ?: run {
|
||||||
val bookSourceFile = File(path, "bookSource.json")
|
val bookSourceFile = File(path, "bookSource.json")
|
||||||
if (bookSourceFile.exists()) {
|
if (bookSourceFile.exists()) {
|
||||||
@@ -145,54 +154,92 @@ object Restore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
fileToListT<RssSource>(path, "rssSources.json")?.let {
|
fileToListT<RssSource>(path, "rssSources.json")?.let {
|
||||||
appDb.rssSourceDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.rssSourceDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileToListT<RssStar>(path, "rssStar.json")?.let {
|
fileToListT<RssStar>(path, "rssStar.json")?.let {
|
||||||
appDb.rssStarDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.rssStarDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileToListT<ReplaceRule>(path, "replaceRule.json")?.let {
|
fileToListT<ReplaceRule>(path, "replaceRule.json")?.let {
|
||||||
appDb.replaceRuleDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.replaceRuleDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileToListT<SearchKeyword>(path, "searchHistory.json")?.let {
|
fileToListT<SearchKeyword>(path, "searchHistory.json")?.let {
|
||||||
appDb.searchKeywordDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.searchKeywordDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileToListT<RuleSub>(path, "sourceSub.json")?.let {
|
fileToListT<RuleSub>(path, "sourceSub.json")?.let {
|
||||||
appDb.ruleSubDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.ruleSubDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileToListT<TxtTocRule>(path, "txtTocRule.json")?.let {
|
fileToListT<TxtTocRule>(path, "txtTocRule.json")?.let {
|
||||||
appDb.txtTocRuleDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.txtTocRuleDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileToListT<HttpTTS>(path, "httpTTS.json")?.let {
|
fileToListT<HttpTTS>(path, "httpTTS.json")?.let {
|
||||||
appDb.httpTTSDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.httpTTSDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileToListT<DictRule>(path, "dictRule.json")?.let {
|
fileToListT<DictRule>(path, "dictRule.json")?.let {
|
||||||
appDb.dictRuleDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.dictRuleDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileToListT<KeyboardAssist>(path, "keyboardAssists.json")?.let {
|
fileToListT<KeyboardAssist>(path, "keyboardAssists.json")?.let {
|
||||||
appDb.keyboardAssistsDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.keyboardAssistsDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fileToListT<ReadRecord>(path, "readRecord.json")?.let {
|
fileToListT<ReadRecord>(path, "readRecord.json")?.let {
|
||||||
it.forEach { readRecord ->
|
it.forEach { readRecord ->
|
||||||
//判断是不是本机记录
|
|
||||||
if (readRecord.deviceId != androidId) {
|
if (readRecord.deviceId != androidId) {
|
||||||
appDb.readRecordDao.insert(readRecord)
|
try {
|
||||||
|
appDb.readRecordDao.insert(readRecord)
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
val time = appDb.readRecordDao
|
val time = appDb.readRecordDao
|
||||||
.getReadTime(readRecord.deviceId, readRecord.bookName, readRecord.bookAuthor)
|
.getReadTime(readRecord.deviceId, readRecord.bookName, readRecord.bookAuthor)
|
||||||
if (time == null || time < readRecord.readTime) {
|
if (time == null || time < readRecord.readTime) {
|
||||||
appDb.readRecordDao.insert(readRecord)
|
try {
|
||||||
|
appDb.readRecordDao.insert(readRecord)
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fileToListT<ReadRecordDetail>(path, "readRecordDetail.json")?.let {
|
fileToListT<ReadRecordDetail>(path, "readRecordDetail.json")?.let {
|
||||||
it.forEach { detail ->
|
it.forEach { detail ->
|
||||||
appDb.readRecordDao.insertDetail(detail)
|
try {
|
||||||
|
appDb.readRecordDao.insertDetail(detail)
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fileToListT<ReadRecordSession>(path, "readRecordSession.json")?.let {
|
fileToListT<ReadRecordSession>(path, "readRecordSession.json")?.let {
|
||||||
it.forEach { session ->
|
it.forEach { session ->
|
||||||
appDb.readRecordDao.insertSession(session)
|
try {
|
||||||
|
appDb.readRecordDao.insertSession(session)
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
File(path, "servers.json").takeIf {
|
File(path, "servers.json").takeIf {
|
||||||
@@ -203,7 +250,10 @@ object Restore {
|
|||||||
json = aes.decryptStr(json)
|
json = aes.decryptStr(json)
|
||||||
}
|
}
|
||||||
GSON.fromJsonArray<Server>(json).getOrNull()?.let {
|
GSON.fromJsonArray<Server>(json).getOrNull()?.let {
|
||||||
appDb.serverDao.insert(*it.toTypedArray())
|
try {
|
||||||
|
appDb.serverDao.insert(*it.toTypedArray())
|
||||||
|
} catch (_: SQLiteConstraintException) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}?.onFailure {
|
}?.onFailure {
|
||||||
AppLog.put("恢复服务器配置出错\n${it.localizedMessage}", it)
|
AppLog.put("恢复服务器配置出错\n${it.localizedMessage}", it)
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import io.legado.app.constant.AppConst.UA_NAME
|
|||||||
import io.legado.app.constant.AppPattern
|
import io.legado.app.constant.AppPattern
|
||||||
import io.legado.app.data.entities.BaseSource
|
import io.legado.app.data.entities.BaseSource
|
||||||
import io.legado.app.data.entities.Book
|
import io.legado.app.data.entities.Book
|
||||||
|
import io.legado.app.exception.NoStackTraceException
|
||||||
import io.legado.app.data.entities.BookChapter
|
import io.legado.app.data.entities.BookChapter
|
||||||
import io.legado.app.help.CacheManager
|
import io.legado.app.help.CacheManager
|
||||||
import io.legado.app.help.ConcurrentRateLimiter
|
import io.legado.app.help.ConcurrentRateLimiter
|
||||||
@@ -668,9 +669,10 @@ class AnalyzeUrl(
|
|||||||
* 上传文件
|
* 上传文件
|
||||||
*/
|
*/
|
||||||
suspend fun upload(fileName: String, file: Any, contentType: String): StrResponse {
|
suspend fun upload(fileName: String, file: Any, contentType: String): StrResponse {
|
||||||
|
val bodyMap = GSON.fromJsonObject<HashMap<String, Any>>(body).getOrNull()
|
||||||
|
?: return getErrStrResponse(NoStackTraceException("请求体不是合法的JSON格式"))
|
||||||
return getProxyClient(proxy).newCallStrResponse(retry) {
|
return getProxyClient(proxy).newCallStrResponse(retry) {
|
||||||
url(urlNoQuery)
|
url(urlNoQuery)
|
||||||
val bodyMap = GSON.fromJsonObject<HashMap<String, Any>>(body).getOrNull()!!
|
|
||||||
bodyMap.forEach { entry ->
|
bodyMap.forEach { entry ->
|
||||||
if (entry.value.toString() == "fileRequest") {
|
if (entry.value.toString() == "fileRequest") {
|
||||||
bodyMap[entry.key] = mapOf(
|
bodyMap[entry.key] = mapOf(
|
||||||
|
|||||||
@@ -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.GlassMediumFlexibleTopAppBar
|
||||||
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
import io.legado.app.ui.widget.components.topbar.GlassTopAppBarDefaults
|
||||||
import io.legado.app.ui.main.bookCoverSharedElementKey
|
import io.legado.app.ui.main.bookCoverSharedElementKey
|
||||||
import kotlinx.coroutines.delay
|
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import org.koin.compose.koinInject
|
import org.koin.compose.koinInject
|
||||||
|
|
||||||
@@ -563,10 +562,7 @@ fun LoadMoreFooter(
|
|||||||
|
|
||||||
LaunchedEffect(isLoading, errorMsg, isEnd) {
|
LaunchedEffect(isLoading, errorMsg, isEnd) {
|
||||||
if (!isLoading && errorMsg == null && !isEnd) {
|
if (!isLoading && errorMsg == null && !isEnd) {
|
||||||
while (true) {
|
onRetry()
|
||||||
onRetry()
|
|
||||||
delay(1000L)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -397,14 +397,14 @@ class BookshelfViewModel(
|
|||||||
|
|
||||||
val uiState: StateFlow<BookshelfUiState> = combine(
|
val uiState: StateFlow<BookshelfUiState> = combine(
|
||||||
dataStateFlow,
|
dataStateFlow,
|
||||||
interactionStateFlow
|
interactionStateFlow,
|
||||||
) { data, interaction ->
|
visibleBooksFlow
|
||||||
|
) { data, interaction, filteredBooks ->
|
||||||
val books = data.books
|
val books = data.books
|
||||||
val groups = data.groups
|
val groups = data.groups
|
||||||
val allGroups = data.allGroups
|
val allGroups = data.allGroups
|
||||||
val previews = data.previews
|
val previews = data.previews
|
||||||
val internal = data.internal
|
val internal = data.internal
|
||||||
val filteredBooks = filterBooks(books, internal.searchKey, internal.isSearchMode)
|
|
||||||
val selectedGroupIndex = groups.indexOfFirst { it.groupId == internal.groupId }
|
val selectedGroupIndex = groups.indexOfFirst { it.groupId == internal.groupId }
|
||||||
.coerceAtLeast(0)
|
.coerceAtLeast(0)
|
||||||
val currentGroupName = allGroups.firstOrNull { it.groupId == internal.groupId }?.groupName
|
val currentGroupName = allGroups.firstOrNull { it.groupId == internal.groupId }?.groupName
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow
|
|||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
@@ -35,6 +36,7 @@ class RssViewModel(
|
|||||||
val effects = _effects.asSharedFlow()
|
val effects = _effects.asSharedFlow()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
_uiState.update { it.copy(isLoading = true) }
|
||||||
initGroupData()
|
initGroupData()
|
||||||
initRssData()
|
initRssData()
|
||||||
}
|
}
|
||||||
@@ -62,7 +64,10 @@ class RssViewModel(
|
|||||||
}
|
}
|
||||||
.flowOn(IO)
|
.flowOn(IO)
|
||||||
.onEach { sources ->
|
.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)
|
.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user