fix: 修复换源配置卡死

This commit is contained in:
HapeLee
2026-06-14 16:43:08 +08:00
parent 7c9b08d21a
commit 4ad6b1396b
2 changed files with 79 additions and 57 deletions
@@ -1,8 +1,15 @@
package io.legado.app.domain.model
import io.legado.app.utils.splitNotBlank
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.buildJsonObject
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
data class BookSearchScope(val raw: String) {
@@ -39,19 +46,11 @@ data class BookSearchScope(val raw: String) {
get() = groups.isEmpty() && sources.isEmpty()
}
@Serializable
data class ScopeSourceItem(
val name: String,
val url: String,
)
@Serializable
private data class SerializedSearchScope(
val type: String = "",
val groups: List<String> = emptyList(),
val sources: List<ScopeSourceItem> = emptyList(),
)
companion object {
fun encodeGroups(groups: List<String>): String {
@@ -59,9 +58,12 @@ data class BookSearchScope(val raw: String) {
return if (selected.isEmpty()) {
""
} else {
scopeJson.encodeToString(
SerializedSearchScope(type = TYPE_GROUP, groups = selected)
)
buildJsonObject {
put("type", JsonPrimitive(TYPE_GROUP))
put("groups", buildJsonArray {
selected.forEach { add(JsonPrimitive(it)) }
})
}.toString()
}
}
@@ -70,9 +72,17 @@ data class BookSearchScope(val raw: String) {
return if (selected.isEmpty()) {
""
} else {
scopeJson.encodeToString(
SerializedSearchScope(type = TYPE_SOURCE, sources = selected)
)
buildJsonObject {
put("type", JsonPrimitive(TYPE_SOURCE))
put("sources", buildJsonArray {
selected.forEach { source ->
add(buildJsonObject {
put("name", JsonPrimitive(source.name))
put("url", JsonPrimitive(source.url))
})
}
})
}.toString()
}
}
@@ -91,15 +101,22 @@ data class BookSearchScope(val raw: String) {
if (!json.startsWith("{") || !json.endsWith("}")) return null
return runCatching {
scopeJson.decodeFromString<SerializedSearchScope>(json)
scopeJson.parseToJsonElement(json).jsonObject
}.getOrNull()?.let { scope ->
when (scope.type) {
when (scope["type"]?.jsonPrimitive?.contentOrNull) {
TYPE_SOURCE -> ParsedSearchScope(
sources = scope.sources.filter { it.url.isNotBlank() }
sources = scope["sources"]
?.safeJsonArray()
?.mapNotNull { it.toSourceItemOrNull() }
.orEmpty()
)
TYPE_GROUP -> ParsedSearchScope(
groups = scope.groups.filter { it.isNotBlank() }
groups = scope["groups"]
?.safeJsonArray()
?.mapNotNull { it.toStringOrNull() }
?.filter { it.isNotBlank() }
.orEmpty()
)
else -> null
@@ -130,12 +147,32 @@ data class BookSearchScope(val raw: String) {
}
}
private fun JsonElement.safeJsonArray() = runCatching {
jsonArray
}.getOrNull()
private fun JsonElement.toStringOrNull() = runCatching {
jsonPrimitive.contentOrNull
}.getOrNull()
private fun JsonElement.toSourceItemOrNull(): ScopeSourceItem? {
return runCatching {
val item = jsonObject
val url = item["url"]?.jsonPrimitive?.contentOrNull
if (url.isNullOrBlank()) {
null
} else {
ScopeSourceItem(
name = item["name"]?.jsonPrimitive?.contentOrNull.orEmpty(),
url = url
)
}
}.getOrNull()
}
private const val TYPE_GROUP = "group"
private const val TYPE_SOURCE = "source"
private val scopeJson = Json {
encodeDefaults = false
ignoreUnknownKeys = true
}
private val scopeJson = Json
}
}
@@ -1,7 +1,9 @@
package io.legado.app.ui.book.changesource
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.snapshots.Snapshot
import androidx.datastore.preferences.core.Preferences
import io.legado.app.data.local.preferences.LocalPreferencesKeys
import io.legado.app.data.local.preferences.LocalPreferencesRepository
import io.legado.app.domain.usecase.ChangeSourceMigrationOptions
@@ -9,9 +11,7 @@ import io.legado.app.ui.config.prefDelegate
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import splitties.init.appCtx
object ChangeSourceConfig {
@@ -113,40 +113,25 @@ object ChangeSourceConfig {
)
init {
runBlocking(Dispatchers.IO) {
Snapshot.withMutableSnapshot {
_searchScope.value =
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_SEARCH_SCOPE, "").first()
_checkAuthor.value =
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_CHECK_AUTHOR, false).first()
_loadInfo.value =
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_INFO, false).first()
_loadToc.value =
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_TOC, false).first()
_loadWordCount.value =
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_WORD_COUNT, false).first()
}
}
observe(LocalPreferencesKeys.CHANGE_SOURCE_SEARCH_SCOPE, "", _searchScope)
observe(LocalPreferencesKeys.CHANGE_SOURCE_CHECK_AUTHOR, false, _checkAuthor)
observe(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_INFO, false, _loadInfo)
observe(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_TOC, false, _loadToc)
observe(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_WORD_COUNT, false, _loadWordCount)
}
private fun <T> observe(
key: Preferences.Key<T>,
defaultValue: T,
state: MutableState<T>,
) {
scope.launch {
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_SEARCH_SCOPE, "")
.collect { Snapshot.withMutableSnapshot { _searchScope.value = it } }
}
scope.launch {
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_CHECK_AUTHOR, false)
.collect { Snapshot.withMutableSnapshot { _checkAuthor.value = it } }
}
scope.launch {
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_INFO, false)
.collect { Snapshot.withMutableSnapshot { _loadInfo.value = it } }
}
scope.launch {
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_TOC, false)
.collect { Snapshot.withMutableSnapshot { _loadToc.value = it } }
}
scope.launch {
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_WORD_COUNT, false)
.collect { Snapshot.withMutableSnapshot { _loadWordCount.value = it } }
repo.getPreference(key, defaultValue)
.collect { value ->
Snapshot.withMutableSnapshot {
state.value = value
}
}
}
}