From 12e7ee9d0caaa56d551908d179788fa637768397 Mon Sep 17 00:00:00 2001 From: arkon Date: Sat, 15 Jul 2023 10:09:46 -0400 Subject: [PATCH] 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. --- .../source/service/SourcePreferences.kt | 2 - .../presentation/browse/GlobalSearchScreen.kt | 54 ++++++++------- .../settings/screen/SettingsBrowseScreen.kt | 4 -- .../migration/search/MigrateSearchScreen.kt | 1 + .../source/globalsearch/GlobalSearchScreen.kt | 3 +- .../globalsearch/GlobalSearchScreenModel.kt | 66 +++++++++---------- .../source/globalsearch/SearchScreenModel.kt | 16 ++--- i18n/src/main/res/values/strings.xml | 1 - 8 files changed, 70 insertions(+), 77 deletions(-) diff --git a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt b/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt index c81fa16329..582dd61d7b 100644 --- a/app/src/main/java/eu/kanade/domain/source/service/SourcePreferences.kt +++ b/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 searchPinnedSourcesOnly() = preferenceStore.getBoolean("search_pinned_sources_only", false) - fun hideInLibraryItems() = preferenceStore.getBoolean("browse_hide_in_library_items", false) } diff --git a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt b/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt index 886f9960a2..4a80724f0d 100644 --- a/app/src/main/java/eu/kanade/presentation/browse/GlobalSearchScreen.kt +++ b/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.tachiyomi.R 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.SourceFilter import eu.kanade.tachiyomi.util.system.LocaleHelper import tachiyomi.domain.manga.model.Manga import tachiyomi.presentation.core.components.material.Divider import tachiyomi.presentation.core.components.material.Scaffold +import tachiyomi.presentation.core.components.material.VerticalDivider import tachiyomi.presentation.core.components.material.padding @Composable fun GlobalSearchScreen( - state: GlobalSearchState, + state: GlobalSearchScreenModel.State, items: Map, navigateUp: () -> Unit, onChangeSearchQuery: (String?) -> Unit, onSearch: (String) -> Unit, - onChangeFilter: (GlobalSearchFilter) -> Unit, + onChangeSearchFilter: (SourceFilter) -> Unit, + onToggleResults: () -> Unit, getManga: @Composable (Manga) -> State, onClickSource: (CatalogueSource) -> Unit, onClickItem: (Manga) -> Unit, @@ -71,13 +73,29 @@ fun GlobalSearchScreen( .padding(horizontal = MaterialTheme.padding.small), horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small), ) { + // TODO: make this UX better; it only applies when triggering a new search FilterChip( - selected = state.searchFilter == GlobalSearchFilter.All, - onClick = { onChangeFilter(GlobalSearchFilter.All) }, + selected = state.sourceFilter == SourceFilter.PinnedOnly, + onClick = { onChangeSearchFilter(SourceFilter.PinnedOnly) }, + leadingIcon = { + Icon( + imageVector = Icons.Outlined.PushPin, + contentDescription = null, + modifier = Modifier + .size(FilterChipDefaults.IconSize), + ) + }, + label = { + Text(text = stringResource(id = R.string.pinned_sources)) + }, + ) + FilterChip( + selected = state.sourceFilter == SourceFilter.All, + onClick = { onChangeSearchFilter(SourceFilter.All) }, leadingIcon = { Icon( imageVector = Icons.Outlined.DoneAll, - contentDescription = "", + contentDescription = null, modifier = Modifier .size(FilterChipDefaults.IconSize), ) @@ -87,29 +105,15 @@ fun GlobalSearchScreen( }, ) - FilterChip( - selected = state.searchFilter == GlobalSearchFilter.PinnedOnly, - onClick = { onChangeFilter(GlobalSearchFilter.PinnedOnly) }, - leadingIcon = { - Icon( - imageVector = Icons.Outlined.PushPin, - contentDescription = "", - modifier = Modifier - .size(FilterChipDefaults.IconSize), - ) - }, - label = { - Text(text = stringResource(id = R.string.pinned_sources)) - }, - ) + VerticalDivider() FilterChip( - selected = state.searchFilter == GlobalSearchFilter.AvailableOnly, - onClick = { onChangeFilter(GlobalSearchFilter.AvailableOnly) }, + selected = state.onlyShowHasResults, + onClick = { onToggleResults() }, leadingIcon = { Icon( imageVector = Icons.Outlined.FilterList, - contentDescription = "", + contentDescription = null, modifier = Modifier .size(FilterChipDefaults.IconSize), ) diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt index 01fb87e7e2..5b3ab28cf3 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/screen/SettingsBrowseScreen.kt @@ -29,10 +29,6 @@ object SettingsBrowseScreen : SearchableSettings { Preference.PreferenceGroup( title = stringResource(R.string.label_sources), preferenceItems = listOf( - Preference.PreferenceItem.SwitchPreference( - pref = sourcePreferences.searchPinnedSourcesOnly(), - title = stringResource(R.string.pref_search_pinned_sources_only), - ), Preference.PreferenceItem.SwitchPreference( pref = sourcePreferences.hideInLibraryItems(), title = stringResource(R.string.pref_hide_in_library_items), diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt index 73ad967800..ae89d9cb00 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt +++ b/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.tachiyomi.ui.manga.MangaScreen +// TODO: this should probably be merged with GlobalSearchScreen somehow to dedupe logic class MigrateSearchScreen(private val mangaId: Long) : Screen() { @Composable diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt index 5f1999378c..78c449fd04 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreen.kt @@ -63,7 +63,8 @@ class GlobalSearchScreen( onChangeSearchQuery = screenModel::updateSearchQuery, onSearch = screenModel::search, getManga = { screenModel.getManga(it) }, - onChangeFilter = screenModel::setFilter, + onChangeSearchFilter = screenModel::setSourceFilter, + onToggleResults = screenModel::toggleFilterResults, onClickSource = { if (!screenModel.incognitoMode.get()) { screenModel.lastUsedSourceId.set(it.id) diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreenModel.kt index 7190987f6f..13ac004686 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreenModel.kt @@ -20,17 +20,17 @@ class GlobalSearchScreenModel( preferences: BasePreferences = Injekt.get(), private val sourcePreferences: SourcePreferences = Injekt.get(), private val sourceManager: SourceManager = Injekt.get(), -) : SearchScreenModel(GlobalSearchState(searchQuery = initialQuery)) { +) : SearchScreenModel(State(searchQuery = initialQuery)) { val incognitoMode = preferences.incognitoMode() val lastUsedSourceId = sourcePreferences.lastUsedSource() - val searchPagerFlow = state.map { Pair(it.searchFilter, it.items) } + val searchPagerFlow = state.map { Pair(it.onlyShowHasResults, it.items) } .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 { extensionFilter = initialExtensionFilter @@ -45,19 +45,12 @@ class GlobalSearchScreenModel( val pinnedSources = sourcePreferences.pinnedSources().get() return sourceManager.getCatalogueSources() + .filter { mutableState.value.sourceFilter != SourceFilter.PinnedOnly || "${it.id}" in pinnedSources } .filter { it.lang in enabledLanguages } .filterNot { "${it.id}" in disabledSources } .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?) { mutableState.update { it.copy(searchQuery = query) @@ -70,27 +63,32 @@ class GlobalSearchScreenModel( } } - fun setFilter(filter: GlobalSearchFilter) { - mutableState.update { it.copy(searchFilter = filter) } - } - override fun getItems(): Map { return mutableState.value.items } -} - -enum class GlobalSearchFilter { - All, PinnedOnly, AvailableOnly -} - -@Immutable -data class GlobalSearchState( - val searchQuery: String? = null, - val searchFilter: GlobalSearchFilter = GlobalSearchFilter.All, - val items: Map = emptyMap(), -) { - - val progress: Int = items.count { it.value !is SearchItemResult.Loading } - - val total: Int = items.size + + fun setSourceFilter(filter: SourceFilter) { + mutableState.update { it.copy(sourceFilter = filter) } + } + + fun toggleFilterResults() { + mutableState.update { + it.copy(onlyShowHasResults = !it.onlyShowHasResults) + } + } + + private fun SearchItemResult.isVisible(onlyShowHasResults: Boolean): Boolean { + return !onlyShowHasResults || (this is SearchItemResult.Success && !this.isEmpty) + } + + @Immutable + data class State( + val searchQuery: String? = null, + val sourceFilter: SourceFilter = SourceFilter.PinnedOnly, + val onlyShowHasResults: Boolean = false, + val items: Map = emptyMap(), + ) { + val progress: Int = items.count { it.value !is SearchItemResult.Loading } + val total: Int = items.size + } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt index 78238b21b8..26ab767c50 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/SearchScreenModel.kt @@ -68,16 +68,7 @@ abstract class SearchScreenModel( val enabledSources = getEnabledSources() 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 @@ -137,6 +128,11 @@ abstract class SearchScreenModel( } } +enum class SourceFilter { + All, + PinnedOnly, +} + sealed class SearchItemResult { object Loading : SearchItemResult() diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index 6984ac54a6..3df318c7e9 100644 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -480,7 +480,6 @@ Track - Only search pinned sources in global search Hide entries already in library