瀏覽代碼

Tweak global search source filtering

Pinned only setting is removed in favor of the UI in the global search screen itself, which defaults to pinned only.
This needs more UX improvements, but I'm not really sure what it should be like right now.
arkon 1 年之前
父節點
當前提交
12e7ee9d0c

+ 0 - 2
app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt

@@ -30,7 +30,5 @@ class SourcePreferences(
 
 
     fun trustedSignatures() = preferenceStore.getStringSet("trusted_signatures", emptySet())
     fun trustedSignatures() = preferenceStore.getStringSet("trusted_signatures", emptySet())
 
 
-    fun searchPinnedSourcesOnly() = preferenceStore.getBoolean("search_pinned_sources_only", false)
-
     fun hideInLibraryItems() = preferenceStore.getBoolean("browse_hide_in_library_items", false)
     fun hideInLibraryItems() = preferenceStore.getBoolean("browse_hide_in_library_items", false)
 }
 }

+ 22 - 18
app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt

@@ -30,23 +30,25 @@ import eu.kanade.presentation.browse.components.GlobalSearchResultItem
 import eu.kanade.presentation.browse.components.GlobalSearchToolbar
 import eu.kanade.presentation.browse.components.GlobalSearchToolbar
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.source.CatalogueSource
 import eu.kanade.tachiyomi.source.CatalogueSource
-import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchFilter
-import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchState
+import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchScreenModel
 import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
 import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SearchItemResult
+import eu.kanade.tachiyomi.ui.browse.source.globalsearch.SourceFilter
 import eu.kanade.tachiyomi.util.system.LocaleHelper
 import eu.kanade.tachiyomi.util.system.LocaleHelper
 import tachiyomi.domain.manga.model.Manga
 import tachiyomi.domain.manga.model.Manga
 import tachiyomi.presentation.core.components.material.Divider
 import tachiyomi.presentation.core.components.material.Divider
 import tachiyomi.presentation.core.components.material.Scaffold
 import tachiyomi.presentation.core.components.material.Scaffold
+import tachiyomi.presentation.core.components.material.VerticalDivider
 import tachiyomi.presentation.core.components.material.padding
 import tachiyomi.presentation.core.components.material.padding
 
 
 @Composable
 @Composable
 fun GlobalSearchScreen(
 fun GlobalSearchScreen(
-    state: GlobalSearchState,
+    state: GlobalSearchScreenModel.State,
     items: Map<CatalogueSource, SearchItemResult>,
     items: Map<CatalogueSource, SearchItemResult>,
     navigateUp: () -> Unit,
     navigateUp: () -> Unit,
     onChangeSearchQuery: (String?) -> Unit,
     onChangeSearchQuery: (String?) -> Unit,
     onSearch: (String) -> Unit,
     onSearch: (String) -> Unit,
-    onChangeFilter: (GlobalSearchFilter) -> Unit,
+    onChangeSearchFilter: (SourceFilter) -> Unit,
+    onToggleResults: () -> Unit,
     getManga: @Composable (Manga) -> State<Manga>,
     getManga: @Composable (Manga) -> State<Manga>,
     onClickSource: (CatalogueSource) -> Unit,
     onClickSource: (CatalogueSource) -> Unit,
     onClickItem: (Manga) -> Unit,
     onClickItem: (Manga) -> Unit,
@@ -71,45 +73,47 @@ fun GlobalSearchScreen(
                         .padding(horizontal = MaterialTheme.padding.small),
                         .padding(horizontal = MaterialTheme.padding.small),
                     horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
                     horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
                 ) {
                 ) {
+                    // TODO: make this UX better; it only applies when triggering a new search
                     FilterChip(
                     FilterChip(
-                        selected = state.searchFilter == GlobalSearchFilter.All,
-                        onClick = { onChangeFilter(GlobalSearchFilter.All) },
+                        selected = state.sourceFilter == SourceFilter.PinnedOnly,
+                        onClick = { onChangeSearchFilter(SourceFilter.PinnedOnly) },
                         leadingIcon = {
                         leadingIcon = {
                             Icon(
                             Icon(
-                                imageVector = Icons.Outlined.DoneAll,
-                                contentDescription = "",
+                                imageVector = Icons.Outlined.PushPin,
+                                contentDescription = null,
                                 modifier = Modifier
                                 modifier = Modifier
                                     .size(FilterChipDefaults.IconSize),
                                     .size(FilterChipDefaults.IconSize),
                             )
                             )
                         },
                         },
                         label = {
                         label = {
-                            Text(text = stringResource(id = R.string.all))
+                            Text(text = stringResource(id = R.string.pinned_sources))
                         },
                         },
                     )
                     )
-
                     FilterChip(
                     FilterChip(
-                        selected = state.searchFilter == GlobalSearchFilter.PinnedOnly,
-                        onClick = { onChangeFilter(GlobalSearchFilter.PinnedOnly) },
+                        selected = state.sourceFilter == SourceFilter.All,
+                        onClick = { onChangeSearchFilter(SourceFilter.All) },
                         leadingIcon = {
                         leadingIcon = {
                             Icon(
                             Icon(
-                                imageVector = Icons.Outlined.PushPin,
-                                contentDescription = "",
+                                imageVector = Icons.Outlined.DoneAll,
+                                contentDescription = null,
                                 modifier = Modifier
                                 modifier = Modifier
                                     .size(FilterChipDefaults.IconSize),
                                     .size(FilterChipDefaults.IconSize),
                             )
                             )
                         },
                         },
                         label = {
                         label = {
-                            Text(text = stringResource(id = R.string.pinned_sources))
+                            Text(text = stringResource(id = R.string.all))
                         },
                         },
                     )
                     )
 
 
+                    VerticalDivider()
+
                     FilterChip(
                     FilterChip(
-                        selected = state.searchFilter == GlobalSearchFilter.AvailableOnly,
-                        onClick = { onChangeFilter(GlobalSearchFilter.AvailableOnly) },
+                        selected = state.onlyShowHasResults,
+                        onClick = { onToggleResults() },
                         leadingIcon = {
                         leadingIcon = {
                             Icon(
                             Icon(
                                 imageVector = Icons.Outlined.FilterList,
                                 imageVector = Icons.Outlined.FilterList,
-                                contentDescription = "",
+                                contentDescription = null,
                                 modifier = Modifier
                                 modifier = Modifier
                                     .size(FilterChipDefaults.IconSize),
                                     .size(FilterChipDefaults.IconSize),
                             )
                             )

+ 0 - 4
app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt

@@ -29,10 +29,6 @@ object SettingsBrowseScreen : SearchableSettings {
             Preference.PreferenceGroup(
             Preference.PreferenceGroup(
                 title = stringResource(R.string.label_sources),
                 title = stringResource(R.string.label_sources),
                 preferenceItems = listOf(
                 preferenceItems = listOf(
-                    Preference.PreferenceItem.SwitchPreference(
-                        pref = sourcePreferences.searchPinnedSourcesOnly(),
-                        title = stringResource(R.string.pref_search_pinned_sources_only),
-                    ),
                     Preference.PreferenceItem.SwitchPreference(
                     Preference.PreferenceItem.SwitchPreference(
                         pref = sourcePreferences.hideInLibraryItems(),
                         pref = sourcePreferences.hideInLibraryItems(),
                         title = stringResource(R.string.pref_hide_in_library_items),
                         title = stringResource(R.string.pref_hide_in_library_items),

+ 1 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt

@@ -10,6 +10,7 @@ import eu.kanade.presentation.browse.MigrateSearchScreen
 import eu.kanade.presentation.util.Screen
 import eu.kanade.presentation.util.Screen
 import eu.kanade.tachiyomi.ui.manga.MangaScreen
 import eu.kanade.tachiyomi.ui.manga.MangaScreen
 
 
+// TODO: this should probably be merged with GlobalSearchScreen somehow to dedupe logic
 class MigrateSearchScreen(private val mangaId: Long) : Screen() {
 class MigrateSearchScreen(private val mangaId: Long) : Screen() {
 
 
     @Composable
     @Composable

+ 2 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt

@@ -63,7 +63,8 @@ class GlobalSearchScreen(
                 onChangeSearchQuery = screenModel::updateSearchQuery,
                 onChangeSearchQuery = screenModel::updateSearchQuery,
                 onSearch = screenModel::search,
                 onSearch = screenModel::search,
                 getManga = { screenModel.getManga(it) },
                 getManga = { screenModel.getManga(it) },
-                onChangeFilter = screenModel::setFilter,
+                onChangeSearchFilter = screenModel::setSourceFilter,
+                onToggleResults = screenModel::toggleFilterResults,
                 onClickSource = {
                 onClickSource = {
                     if (!screenModel.incognitoMode.get()) {
                     if (!screenModel.incognitoMode.get()) {
                         screenModel.lastUsedSourceId.set(it.id)
                         screenModel.lastUsedSourceId.set(it.id)

+ 28 - 30
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreenModel.kt

@@ -20,17 +20,17 @@ class GlobalSearchScreenModel(
     preferences: BasePreferences = Injekt.get(),
     preferences: BasePreferences = Injekt.get(),
     private val sourcePreferences: SourcePreferences = Injekt.get(),
     private val sourcePreferences: SourcePreferences = Injekt.get(),
     private val sourceManager: SourceManager = Injekt.get(),
     private val sourceManager: SourceManager = Injekt.get(),
-) : SearchScreenModel<GlobalSearchState>(GlobalSearchState(searchQuery = initialQuery)) {
+) : SearchScreenModel<GlobalSearchScreenModel.State>(State(searchQuery = initialQuery)) {
 
 
     val incognitoMode = preferences.incognitoMode()
     val incognitoMode = preferences.incognitoMode()
     val lastUsedSourceId = sourcePreferences.lastUsedSource()
     val lastUsedSourceId = sourcePreferences.lastUsedSource()
 
 
-    val searchPagerFlow = state.map { Pair(it.searchFilter, it.items) }
+    val searchPagerFlow = state.map { Pair(it.onlyShowHasResults, it.items) }
         .distinctUntilChanged()
         .distinctUntilChanged()
-        .map { (filter, items) ->
-            items
-                .filter { (source, result) -> isSourceVisible(filter, source, result) }
-        }.stateIn(ioCoroutineScope, SharingStarted.Lazily, state.value.items)
+        .map { (onlyShowHasResults, items) ->
+            items.filter { (_, result) -> result.isVisible(onlyShowHasResults) }
+        }
+        .stateIn(ioCoroutineScope, SharingStarted.Lazily, state.value.items)
 
 
     init {
     init {
         extensionFilter = initialExtensionFilter
         extensionFilter = initialExtensionFilter
@@ -45,19 +45,12 @@ class GlobalSearchScreenModel(
         val pinnedSources = sourcePreferences.pinnedSources().get()
         val pinnedSources = sourcePreferences.pinnedSources().get()
 
 
         return sourceManager.getCatalogueSources()
         return sourceManager.getCatalogueSources()
+            .filter { mutableState.value.sourceFilter != SourceFilter.PinnedOnly || "${it.id}" in pinnedSources }
             .filter { it.lang in enabledLanguages }
             .filter { it.lang in enabledLanguages }
             .filterNot { "${it.id}" in disabledSources }
             .filterNot { "${it.id}" in disabledSources }
             .sortedWith(compareBy({ "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" }))
             .sortedWith(compareBy({ "${it.id}" !in pinnedSources }, { "${it.name.lowercase()} (${it.lang})" }))
     }
     }
 
 
-    private fun isSourceVisible(filter: GlobalSearchFilter, source: CatalogueSource, result: SearchItemResult): Boolean {
-        return when (filter) {
-            GlobalSearchFilter.AvailableOnly -> result is SearchItemResult.Success && !result.isEmpty
-            GlobalSearchFilter.PinnedOnly -> "${source.id}" in sourcePreferences.pinnedSources().get()
-            GlobalSearchFilter.All -> true
-        }
-    }
-
     override fun updateSearchQuery(query: String?) {
     override fun updateSearchQuery(query: String?) {
         mutableState.update {
         mutableState.update {
             it.copy(searchQuery = query)
             it.copy(searchQuery = query)
@@ -70,27 +63,32 @@ class GlobalSearchScreenModel(
         }
         }
     }
     }
 
 
-    fun setFilter(filter: GlobalSearchFilter) {
-        mutableState.update { it.copy(searchFilter = filter) }
-    }
-
     override fun getItems(): Map<CatalogueSource, SearchItemResult> {
     override fun getItems(): Map<CatalogueSource, SearchItemResult> {
         return mutableState.value.items
         return mutableState.value.items
     }
     }
-}
 
 
-enum class GlobalSearchFilter {
-    All, PinnedOnly, AvailableOnly
-}
+    fun setSourceFilter(filter: SourceFilter) {
+        mutableState.update { it.copy(sourceFilter = filter) }
+    }
 
 
-@Immutable
-data class GlobalSearchState(
-    val searchQuery: String? = null,
-    val searchFilter: GlobalSearchFilter = GlobalSearchFilter.All,
-    val items: Map<CatalogueSource, SearchItemResult> = emptyMap(),
-) {
+    fun toggleFilterResults() {
+        mutableState.update {
+            it.copy(onlyShowHasResults = !it.onlyShowHasResults)
+        }
+    }
 
 
-    val progress: Int = items.count { it.value !is SearchItemResult.Loading }
+    private fun SearchItemResult.isVisible(onlyShowHasResults: Boolean): Boolean {
+        return !onlyShowHasResults || (this is SearchItemResult.Success && !this.isEmpty)
+    }
 
 
-    val total: Int = items.size
+    @Immutable
+    data class State(
+        val searchQuery: String? = null,
+        val sourceFilter: SourceFilter = SourceFilter.PinnedOnly,
+        val onlyShowHasResults: Boolean = false,
+        val items: Map<CatalogueSource, SearchItemResult> = emptyMap(),
+    ) {
+        val progress: Int = items.count { it.value !is SearchItemResult.Loading }
+        val total: Int = items.size
+    }
 }
 }

+ 6 - 10
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt

@@ -68,16 +68,7 @@ abstract class SearchScreenModel<T>(
         val enabledSources = getEnabledSources()
         val enabledSources = getEnabledSources()
 
 
         if (filter.isEmpty()) {
         if (filter.isEmpty()) {
-            val shouldSearchPinnedOnly = sourcePreferences.searchPinnedSourcesOnly().get()
-            val pinnedSources = sourcePreferences.pinnedSources().get()
-
-            return enabledSources.filter {
-                if (shouldSearchPinnedOnly) {
-                    "${it.id}" in pinnedSources
-                } else {
-                    true
-                }
-            }
+            return enabledSources
         }
         }
 
 
         return extensionManager.installedExtensionsFlow.value
         return extensionManager.installedExtensionsFlow.value
@@ -137,6 +128,11 @@ abstract class SearchScreenModel<T>(
     }
     }
 }
 }
 
 
+enum class SourceFilter {
+    All,
+    PinnedOnly,
+}
+
 sealed class SearchItemResult {
 sealed class SearchItemResult {
     object Loading : SearchItemResult()
     object Loading : SearchItemResult()
 
 

+ 0 - 1
i18n/src/main/res/values/strings.xml

@@ -480,7 +480,6 @@
     <string name="action_track">Track</string>
     <string name="action_track">Track</string>
 
 
       <!-- Browse section -->
       <!-- Browse section -->
-    <string name="pref_search_pinned_sources_only">Only search pinned sources in global search</string>
     <string name="pref_hide_in_library_items">Hide entries already in library</string>
     <string name="pref_hide_in_library_items">Hide entries already in library</string>
 
 
       <!-- Backup section -->
       <!-- Backup section -->