fix: 修复换源配置卡死
This commit is contained in:
1 parent
7c9b08d21a
commit
4ad6b1396b
2 files changed
+76
-54
No files matched your search
@@ -1,8 +1,15 @@
|
|||||||
package io.legado.app.domain.model
|
package io.legado.app.domain.model
|
||||||
|
|
||||||
import io.legado.app.utils.splitNotBlank
|
import io.legado.app.utils.splitNotBlank
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
import kotlinx.serialization.json.Json
|
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) {
|
data class BookSearchScope(val raw: String) {
|
||||||
|
|
||||||
@@ -39,19 +46,11 @@ data class BookSearchScope(val raw: String) {
|
|||||||
get() = groups.isEmpty() && sources.isEmpty()
|
get() = groups.isEmpty() && sources.isEmpty()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class ScopeSourceItem(
|
data class ScopeSourceItem(
|
||||||
val name: String,
|
val name: String,
|
||||||
val url: String,
|
val url: String,
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
|
||||||
private data class SerializedSearchScope(
|
|
||||||
val type: String = "",
|
|
||||||
val groups: List<String> = emptyList(),
|
|
||||||
val sources: List<ScopeSourceItem> = emptyList(),
|
|
||||||
)
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|
||||||
fun encodeGroups(groups: List<String>): String {
|
fun encodeGroups(groups: List<String>): String {
|
||||||
@@ -59,9 +58,12 @@ data class BookSearchScope(val raw: String) {
|
|||||||
return if (selected.isEmpty()) {
|
return if (selected.isEmpty()) {
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
scopeJson.encodeToString(
|
buildJsonObject {
|
||||||
SerializedSearchScope(type = TYPE_GROUP, groups = selected)
|
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()) {
|
return if (selected.isEmpty()) {
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
scopeJson.encodeToString(
|
buildJsonObject {
|
||||||
SerializedSearchScope(type = TYPE_SOURCE, sources = selected)
|
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
|
if (!json.startsWith("{") || !json.endsWith("}")) return null
|
||||||
|
|
||||||
return runCatching {
|
return runCatching {
|
||||||
scopeJson.decodeFromString<SerializedSearchScope>(json)
|
scopeJson.parseToJsonElement(json).jsonObject
|
||||||
}.getOrNull()?.let { scope ->
|
}.getOrNull()?.let { scope ->
|
||||||
when (scope.type) {
|
when (scope["type"]?.jsonPrimitive?.contentOrNull) {
|
||||||
TYPE_SOURCE -> ParsedSearchScope(
|
TYPE_SOURCE -> ParsedSearchScope(
|
||||||
sources = scope.sources.filter { it.url.isNotBlank() }
|
sources = scope["sources"]
|
||||||
|
?.safeJsonArray()
|
||||||
|
?.mapNotNull { it.toSourceItemOrNull() }
|
||||||
|
.orEmpty()
|
||||||
)
|
)
|
||||||
|
|
||||||
TYPE_GROUP -> ParsedSearchScope(
|
TYPE_GROUP -> ParsedSearchScope(
|
||||||
groups = scope.groups.filter { it.isNotBlank() }
|
groups = scope["groups"]
|
||||||
|
?.safeJsonArray()
|
||||||
|
?.mapNotNull { it.toStringOrNull() }
|
||||||
|
?.filter { it.isNotBlank() }
|
||||||
|
.orEmpty()
|
||||||
)
|
)
|
||||||
|
|
||||||
else -> null
|
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_GROUP = "group"
|
||||||
private const val TYPE_SOURCE = "source"
|
private const val TYPE_SOURCE = "source"
|
||||||
private val scopeJson = Json {
|
private val scopeJson = Json
|
||||||
encodeDefaults = false
|
|
||||||
ignoreUnknownKeys = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,9 @@
|
|||||||
package io.legado.app.ui.book.changesource
|
package io.legado.app.ui.book.changesource
|
||||||
|
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.snapshots.Snapshot
|
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.LocalPreferencesKeys
|
||||||
import io.legado.app.data.local.preferences.LocalPreferencesRepository
|
import io.legado.app.data.local.preferences.LocalPreferencesRepository
|
||||||
import io.legado.app.domain.usecase.ChangeSourceMigrationOptions
|
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.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.flow.first
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import splitties.init.appCtx
|
import splitties.init.appCtx
|
||||||
|
|
||||||
object ChangeSourceConfig {
|
object ChangeSourceConfig {
|
||||||
@@ -113,40 +113,25 @@ object ChangeSourceConfig {
|
|||||||
)
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
runBlocking(Dispatchers.IO) {
|
observe(LocalPreferencesKeys.CHANGE_SOURCE_SEARCH_SCOPE, "", _searchScope)
|
||||||
Snapshot.withMutableSnapshot {
|
observe(LocalPreferencesKeys.CHANGE_SOURCE_CHECK_AUTHOR, false, _checkAuthor)
|
||||||
_searchScope.value =
|
observe(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_INFO, false, _loadInfo)
|
||||||
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_SEARCH_SCOPE, "").first()
|
observe(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_TOC, false, _loadToc)
|
||||||
_checkAuthor.value =
|
observe(LocalPreferencesKeys.CHANGE_SOURCE_LOAD_WORD_COUNT, false, _loadWordCount)
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun <T> observe(
|
||||||
|
key: Preferences.Key<T>,
|
||||||
|
defaultValue: T,
|
||||||
|
state: MutableState<T>,
|
||||||
|
) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
repo.getPreference(LocalPreferencesKeys.CHANGE_SOURCE_SEARCH_SCOPE, "")
|
repo.getPreference(key, defaultValue)
|
||||||
.collect { Snapshot.withMutableSnapshot { _searchScope.value = it } }
|
.collect { value ->
|
||||||
|
Snapshot.withMutableSnapshot {
|
||||||
|
state.value = value
|
||||||
}
|
}
|
||||||
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 } }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user