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.

(cherry picked from commit 12e7ee9d0caaa56d551908d179788fa637768397)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/migration/search/MigrateSearchScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/globalsearch/GlobalSearchScreenModel.kt
This commit is contained in:
arkon 2023-07-15 10:09:46 -04:00 committed by Jobobby04
parent bb4bf27db0
commit efb0d003f8
8 changed files with 69 additions and 80 deletions

View File

@ -30,8 +30,6 @@ 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)
// SY -->

View File

@ -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<CatalogueSource, SearchItemResult>,
navigateUp: () -> Unit,
onChangeSearchQuery: (String?) -> Unit,
onSearch: (String) -> Unit,
onChangeFilter: (GlobalSearchFilter) -> Unit,
onChangeSearchFilter: (SourceFilter) -> Unit,
onToggleResults: () -> Unit,
getManga: @Composable (Manga) -> State<Manga>,
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),
)

View File

@ -101,10 +101,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),

View File

@ -11,6 +11,7 @@ import eu.kanade.presentation.util.Screen
import eu.kanade.tachiyomi.ui.browse.migration.advanced.process.MigrationListScreen
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, private val validSources: List<Long>) : Screen() {
@Composable

View File

@ -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)

View File

@ -20,17 +20,17 @@ class GlobalSearchScreenModel(
preferences: BasePreferences = Injekt.get(),
private val sourcePreferences: SourcePreferences = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(),
) : SearchScreenModel<GlobalSearchState>(GlobalSearchState(searchQuery = initialQuery)) {
) : SearchScreenModel<GlobalSearchScreenModel.State>(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
@ -44,31 +44,11 @@ class GlobalSearchScreenModel(
val disabledSources = sourcePreferences.disabledSources().get()
val pinnedSources = sourcePreferences.pinnedSources().get()
// SY -->
val shouldSearchPinnedOnly = sourcePreferences.searchPinnedSourcesOnly().get()
// SY <--
return sourceManager.getVisibleCatalogueSources()
.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})" }))
// SY -->
.filter {
if (shouldSearchPinnedOnly) {
"${it.id}" in pinnedSources
} else {
true
}
}
// SY <--
}
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?) {
@ -83,27 +63,32 @@ class GlobalSearchScreenModel(
}
}
fun setFilter(filter: GlobalSearchFilter) {
mutableState.update { it.copy(searchFilter = filter) }
}
override fun getItems(): Map<CatalogueSource, SearchItemResult> {
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<CatalogueSource, SearchItemResult> = 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<CatalogueSource, SearchItemResult> = emptyMap(),
) {
val progress: Int = items.count { it.value !is SearchItemResult.Loading }
val total: Int = items.size
}
}

View File

@ -133,6 +133,11 @@ abstract class SearchScreenModel<T>(
}
}
enum class SourceFilter {
All,
PinnedOnly,
}
sealed class SearchItemResult {
object Loading : SearchItemResult()

View File

@ -480,7 +480,6 @@
<string name="action_track">Track</string>
<!-- 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>
<!-- Backup section -->