Bläddra i källkod

feat: 书源外部导入问题

HapeLee 2 månader sedan
förälder
incheckning
3e3d86b667

+ 85 - 35
app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt

@@ -1,7 +1,9 @@
 package io.legado.app.ui.book.source.manage
 
 import android.app.Application
+import android.net.Uri
 import android.text.TextUtils
+import androidx.core.net.toUri
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import com.google.gson.JsonParser
@@ -31,9 +33,11 @@ import io.legado.app.utils.GSON
 import io.legado.app.utils.NetworkUtils
 import io.legado.app.utils.fromJsonArray
 import io.legado.app.utils.fromJsonObject
+import io.legado.app.utils.inputStream
 import io.legado.app.utils.isAbsUrl
 import io.legado.app.utils.isJsonArray
 import io.legado.app.utils.isJsonObject
+import io.legado.app.utils.isUri
 import io.legado.app.utils.splitNotBlank
 import kotlinx.collections.immutable.toImmutableList
 import kotlinx.collections.immutable.toImmutableSet
@@ -107,37 +111,58 @@ class BookSourceViewModel(
         }
     }
 
-    private val listState = combine(
+    private val sourceFilter = combine(searchKey, filter) { query, activeFilter ->
+        SourceFilter(query, activeFilter)
+    }
+
+    private val sourceSort = combine(sort, sortAscending, groupByDomain) {
+            activeSort,
+            ascending,
+            byDomain,
+        ->
+        SourceSort(activeSort, ascending, byDomain)
+    }
+
+    private val importedOrFilteredItems = combine(
         repository.flowAll(),
-        repository.flowGroups(),
-        searchKey,
-        isSearchMode,
-        filter,
-        selectedIds,
-        sort,
-        sortAscending,
-        groupByDomain,
         localItems,
-        enabledOverrides,
-    ) { values ->
-        @Suppress("UNCHECKED_CAST")
-        val sourceItems = (values[0] as List<BookSourcePart>)
-        val groups = values[1] as List<String>
-        val query = values[2] as String
-        val searchMode = values[3] as Boolean
-        val activeFilter = values[4] as String?
-        val selected = values[5] as Set<String>
-        val activeSort = values[6] as BookSourceSort
-        val ascending = values[7] as Boolean
-        val byDomain = values[8] as Boolean
-        val local = values[9] as List<BookSourcePart>?
-        val pendingEnabled = values[10] as Map<String, Boolean>
-        val visible = if (local == null) {
-            sourceItems.filterFor(activeFilter, query).sortFor(activeSort, ascending, byDomain)
+        sourceFilter,
+    ) { sourceItems, local, activeFilter ->
+        if (local == null) {
+            sourceItems.filterFor(activeFilter.name, activeFilter.query)
         } else {
             val latestById = sourceItems.associateBy { it.bookSourceUrl }
             local.mapNotNull { latestById[it.bookSourceUrl] }
         }
+    }
+
+    private val visibleItems = combine(importedOrFilteredItems, localItems, sourceSort) {
+            items,
+            local,
+            activeSort,
+        ->
+        if (local == null) {
+            items.sortFor(activeSort.sort, activeSort.ascending, activeSort.groupByDomain)
+        } else {
+            items
+        }
+    }
+
+    private val listConfiguration = combine(
+        sourceFilter,
+        isSearchMode,
+        selectedIds,
+        sourceSort,
+        enabledOverrides,
+    ) { activeFilter, searchMode, selected, activeSort, pendingEnabled ->
+        ListConfiguration(activeFilter, searchMode, selected, activeSort, pendingEnabled)
+    }
+
+    private val listState = combine(
+        visibleItems,
+        repository.flowGroups(),
+        listConfiguration,
+    ) { visible, groups, configuration ->
         BookSourceUiState(
             items = visible.map { source ->
                 BookSourceItemUi(
@@ -145,27 +170,48 @@ class BookSourceViewModel(
                     domain = NetworkUtils.getSubDomainOrNull(source.bookSourceUrl) ?: "#",
                     name = source.bookSourceName,
                     group = source.bookSourceGroup,
-                    enabled = pendingEnabled[source.bookSourceUrl] ?: source.enabled,
+                    enabled = configuration.enabledOverrides[source.bookSourceUrl] ?: source.enabled,
                     enabledExplore = source.enabledExplore,
                     hasLoginUrl = source.hasLoginUrl,
                     hasExploreUrl = source.hasExploreUrl,
                     customOrder = source.customOrder,
                 )
             }.toImmutableList(),
-            selectedIds = selected.intersect(visible.map { it.bookSourceUrl }.toSet())
+            selectedIds = configuration.selectedIds.intersect(visible.map { it.bookSourceUrl }.toSet())
                 .toImmutableSet(),
-            searchKey = query,
-            groupFilterName = activeFilter?.displayName(application),
-            activeFilter = activeFilter,
+            searchKey = configuration.filter.query,
+            groupFilterName = configuration.filter.name?.displayName(application),
+            activeFilter = configuration.filter.name,
             groups = groups.toImmutableList(),
-            sort = activeSort,
-            sortAscending = ascending,
-            groupByDomain = byDomain,
-            interaction = io.legado.app.ui.widget.components.list.InteractionState(isSearchMode = searchMode),
+            sort = configuration.sort.sort,
+            sortAscending = configuration.sort.ascending,
+            groupByDomain = configuration.sort.groupByDomain,
+            interaction = io.legado.app.ui.widget.components.list.InteractionState(
+                isSearchMode = configuration.isSearchMode,
+            ),
         )
     }.flowOn(Dispatchers.Default)
         .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5_000), BookSourceUiState())
 
+    private data class SourceFilter(
+        val query: String,
+        val name: String?,
+    )
+
+    private data class SourceSort(
+        val sort: BookSourceSort,
+        val ascending: Boolean,
+        val groupByDomain: Boolean,
+    )
+
+    private data class ListConfiguration(
+        val filter: SourceFilter,
+        val isSearchMode: Boolean,
+        val selectedIds: Set<String>,
+        val sort: SourceSort,
+        val enabledOverrides: Map<String, Boolean>,
+    )
+
     val uiState = combine(
         listState,
         importState,
@@ -401,6 +447,10 @@ class BookSourceViewModel(
                             )
                         } else url(input)
                     }.decompressed().text("utf-8")
+                } else if (input.isUri()) {
+                    input.toUri().inputStream(application).getOrThrow()
+                        .bufferedReader()
+                        .use { it.readText() }
                 } else input
                 val sources = parseImportSources(text)
                 val settings = otherSettingsGateway.currentSettings
@@ -523,7 +573,7 @@ class BookSourceViewModel(
         }
     }
 
-    private fun exportSources(uri: android.net.Uri, ids: Set<String>) = launch {
+    private fun exportSources(uri: Uri, ids: Set<String>) = launch {
         runCatching {
             val selected = repository.getAll().filter { ids.isEmpty() || it.bookSourceUrl in ids }
             application.contentResolver.openOutputStream(uri)?.bufferedWriter()