Add filtering options for pinned sources and empty results in search menu

This commit is contained in:
MuhamadSyabitHidayattulloh
2025-10-31 10:28:00 +07:00
committed by Koitharu
parent 5701862661
commit 119b7c2ac7
5 changed files with 68 additions and 10 deletions

View File

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

View File

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

View File

@@ -62,6 +62,8 @@ class SearchViewModel @Inject constructor(
val kind = savedStateHandle.get<SearchKind>(AppRouter.KEY_KIND) ?: SearchKind.SIMPLE
private var includeDisabledSources = MutableStateFlow(false)
private var pinnedOnly = MutableStateFlow(false)
private var hideEmpty = MutableStateFlow(false)
private val results = MutableStateFlow<List<SearchResultsListModel>>(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 {

View File

@@ -33,6 +33,20 @@
android:title="@string/genre" />
</group>
<group android:id="@+id/group_search_filters">
<item
android:id="@+id/action_filter_pinned_only"
android:checkable="true"
android:title="@string/pinned_sources_only" />
<item
android:id="@+id/action_filter_hide_empty"
android:checkable="true"
android:title="@string/hide_empty_sources" />
</group>
</menu>
</item>

View File

@@ -210,6 +210,8 @@
<string name="disabled">Disabled</string>
<string name="reset_filter">Reset filter</string>
<string name="enter_name">Enter name</string>
<string name="pinned_sources_only">Pinned sources only</string>
<string name="hide_empty_sources">Hide empty sources</string>
<string name="onboard_text">Select languages which you want to read manga. You can change it later in settings.</string>
<string name="never">Never</string>
<string name="only_using_wifi">Only on Wi-Fi</string>