fix: 修复换源配置卡死
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user