From 119b7c2ac74863435615bfbec5cbaf5d6d1a661a Mon Sep 17 00:00:00 2001 From: MuhamadSyabitHidayattulloh Date: Fri, 31 Oct 2025 10:28:00 +0700 Subject: [PATCH] Add filtering options for pinned sources and empty results in search menu --- .../kotatsu/search/ui/multi/SearchActivity.kt | 2 +- .../search/ui/multi/SearchKindMenuProvider.kt | 17 +++++++- .../search/ui/multi/SearchViewModel.kt | 43 +++++++++++++++---- app/src/main/res/menu/opt_search_kind.xml | 14 ++++++ app/src/main/res/values/strings.xml | 2 + 5 files changed, 68 insertions(+), 10 deletions(-) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt index dd1b9b33f..6f882283b 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchActivity.kt @@ -94,7 +94,7 @@ class SearchActivity : setDisplayHomeAsUp(isEnabled = true, showUpAsClose = false) supportActionBar?.setSubtitle(R.string.search_results) - addMenuProvider(SearchKindMenuProvider(this, viewModel.query, viewModel.kind)) + addMenuProvider(SearchKindMenuProvider(this, viewModel, viewModel.query, viewModel.kind)) viewModel.list.observe(this, adapter) viewModel.onError.observeEvent(this, SnackbarErrorObserver(viewBinding.recyclerView, null)) diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchKindMenuProvider.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchKindMenuProvider.kt index 47f70e6f0..8db424dd1 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchKindMenuProvider.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchKindMenuProvider.kt @@ -11,8 +11,9 @@ import org.koitharu.kotatsu.search.domain.SearchKind class SearchKindMenuProvider( private val activity: SearchActivity, + private val viewModel: SearchViewModel, private val query: String, - private val kind: SearchKind + private val kind: SearchKind, ) : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { @@ -32,6 +33,20 @@ class SearchKindMenuProvider( } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { + when (menuItem.itemId) { + R.id.action_filter_pinned_only -> { + menuItem.isChecked = !menuItem.isChecked + viewModel.setPinnedOnly(menuItem.isChecked) + return true + } + + R.id.action_filter_hide_empty -> { + menuItem.isChecked = !menuItem.isChecked + viewModel.setHideEmpty(menuItem.isChecked) + return true + } + } + val newKind = when (menuItem.itemId) { R.id.action_kind_simple -> SearchKind.SIMPLE R.id.action_kind_title -> SearchKind.TITLE diff --git a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchViewModel.kt b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchViewModel.kt index 9612025b7..1e64b9080 100644 --- a/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchViewModel.kt +++ b/app/src/main/kotlin/org/koitharu/kotatsu/search/ui/multi/SearchViewModel.kt @@ -62,6 +62,8 @@ class SearchViewModel @Inject constructor( val kind = savedStateHandle.get(AppRouter.KEY_KIND) ?: SearchKind.SIMPLE private var includeDisabledSources = MutableStateFlow(false) + private var pinnedOnly = MutableStateFlow(false) + private var hideEmpty = MutableStateFlow(false) private val results = MutableStateFlow>(emptyList()) private var searchJob: Job? = null @@ -70,9 +72,15 @@ class SearchViewModel @Inject constructor( results, isLoading.dropWhile { !it }, includeDisabledSources, - ) { list, loading, includeDisabled -> + hideEmpty, + ) { list, loading, includeDisabled, hideEmptyVal -> + val filteredList = if (hideEmptyVal) { + list.filter { it.list.isNotEmpty() } + } else { + list + } when { - list.isEmpty() -> listOf( + filteredList.isEmpty() -> listOf( when { loading -> LoadingState else -> EmptyState( @@ -84,9 +92,9 @@ class SearchViewModel @Inject constructor( }, ) - loading -> list + LoadingFooter() - includeDisabled -> list - else -> list + ButtonFooter(R.string.search_disabled_sources) + loading -> filteredList + LoadingFooter() + includeDisabled -> filteredList + else -> filteredList + ButtonFooter(R.string.search_disabled_sources) } }.stateIn(viewModelScope + Dispatchers.Default, SharingStarted.Eagerly, listOf(LoadingState)) @@ -114,6 +122,17 @@ class SearchViewModel @Inject constructor( doSearch() } + fun setPinnedOnly(value: Boolean) { + if (pinnedOnly.value != value) { + pinnedOnly.value = value + retry() + } + } + + fun setHideEmpty(value: Boolean) { + hideEmpty.value = value + } + fun continueSearch() { if (includeDisabledSources.value) { return @@ -122,8 +141,12 @@ class SearchViewModel @Inject constructor( searchJob = launchLoadingJob(Dispatchers.Default) { includeDisabledSources.value = true prevJob?.join() - val sources = sourcesRepository.getDisabledSources() - .sortedByDescending { it.priority() } + val sources = if (pinnedOnly.value) { + emptyList() + } else { + sourcesRepository.getDisabledSources() + .sortedByDescending { it.priority() } + } val semaphore = Semaphore(MAX_PARALLELISM) sources.map { source -> launch { @@ -142,7 +165,11 @@ class SearchViewModel @Inject constructor( appendResult(searchHistory()) appendResult(searchFavorites()) appendResult(searchLocal()) - val sources = sourcesRepository.getEnabledSources() + val sources = if (pinnedOnly.value) { + sourcesRepository.getPinnedSources().toList() + } else { + sourcesRepository.getEnabledSources() + } val semaphore = Semaphore(MAX_PARALLELISM) sources.map { source -> launch { diff --git a/app/src/main/res/menu/opt_search_kind.xml b/app/src/main/res/menu/opt_search_kind.xml index 157dc309d..ca992d8d0 100644 --- a/app/src/main/res/menu/opt_search_kind.xml +++ b/app/src/main/res/menu/opt_search_kind.xml @@ -33,6 +33,20 @@ android:title="@string/genre" /> + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f390838e8..241e4259c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -210,6 +210,8 @@ Disabled Reset filter Enter name + Pinned sources only + Hide empty sources Select languages which you want to read manga. You can change it later in settings. Never Only on Wi-Fi