Add category filters for Updates tab (#3589)
* Add category filters for Updates tab Works just like Library Updates. Provides a tab in the Updates tab filter dialog to allow for including & excluding of updates belonging to a title from a given category. Excludes overpower Includes, as with Library Updates. A little helpful text informs users about this. * Changelog * [skip ci] Remove unused string Leftover from before I realized the Default category will always be available for in-/exclusion. * Make :includeEmpty/:excludeEmpty true Boolean params * "include" -> "included" Consistency with #3607. * Remove DISTINCT from getRecentUpdatesWithFilters Apparently not needed anymore (though it was required at some point). * [skip ci] Fix Changelog
This commit is contained in:
@@ -14,12 +14,19 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastForEachIndexed
|
||||
import eu.kanade.presentation.category.visualName
|
||||
import eu.kanade.presentation.components.TabbedDialog
|
||||
import eu.kanade.presentation.components.TabbedDialogPaddings
|
||||
import eu.kanade.tachiyomi.ui.updates.UpdatesSettingsViewModel
|
||||
import tachiyomi.core.common.preference.TriState
|
||||
import tachiyomi.core.common.preference.getAndSet
|
||||
import tachiyomi.domain.updates.service.UpdatesPreferences
|
||||
import tachiyomi.i18n.MR
|
||||
@@ -27,6 +34,7 @@ import tachiyomi.presentation.core.components.SettingsItemsPaddings
|
||||
import tachiyomi.presentation.core.components.TriStateItem
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
import tachiyomi.presentation.core.util.collectAsState
|
||||
|
||||
@Composable
|
||||
@@ -38,14 +46,18 @@ fun UpdatesFilterDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
tabTitles = listOf(
|
||||
stringResource(MR.strings.action_filter),
|
||||
stringResource(MR.strings.categories),
|
||||
),
|
||||
) {
|
||||
) { page ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(vertical = TabbedDialogPaddings.Vertical)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
FilterSheet(viewModel = viewModel)
|
||||
when (page) {
|
||||
0 -> FilterSheet(viewModel = viewModel)
|
||||
1 -> CategoryFilterSheet(viewModel = viewModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -108,3 +120,55 @@ private fun ColumnScope.FilterSheet(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.CategoryFilterSheet(
|
||||
viewModel: UpdatesSettingsViewModel,
|
||||
) {
|
||||
Text(
|
||||
stringResource(MR.strings.pref_filter_update_categories_details),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
horizontal = SettingsItemsPaddings.Horizontal,
|
||||
vertical = SettingsItemsPaddings.Vertical,
|
||||
),
|
||||
)
|
||||
|
||||
HorizontalDivider(modifier = Modifier.padding(MaterialTheme.padding.extraSmall))
|
||||
|
||||
val allCategories by viewModel.getCategories.subscribe().collectAsState(initial = emptyList())
|
||||
|
||||
if (allCategories.isEmpty()) {
|
||||
// since it includes the system category, this should only happen when loading is required
|
||||
LoadingScreen(modifier = Modifier.padding(16.dp))
|
||||
return
|
||||
}
|
||||
|
||||
val excluded by viewModel.updatesPreferences.filterExcludedCategories.collectAsState()
|
||||
val included by viewModel.updatesPreferences.filterIncludedCategories.collectAsState()
|
||||
|
||||
val selected = remember {
|
||||
allCategories.map { category ->
|
||||
when (category.id) {
|
||||
in included -> TriState.ENABLED_IS
|
||||
in excluded -> TriState.ENABLED_NOT
|
||||
else -> TriState.DISABLED
|
||||
}
|
||||
}.toMutableStateList()
|
||||
}
|
||||
|
||||
Column {
|
||||
allCategories.fastForEachIndexed { idx, category ->
|
||||
val state = selected[idx]
|
||||
TriStateItem(
|
||||
label = category.visualName,
|
||||
state = state,
|
||||
onClick = {
|
||||
selected[idx] = state.next()
|
||||
viewModel.cycleCategory(category)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,14 +4,32 @@ import androidx.lifecycle.ViewModel
|
||||
import tachiyomi.core.common.preference.Preference
|
||||
import tachiyomi.core.common.preference.TriState
|
||||
import tachiyomi.core.common.preference.getAndSet
|
||||
import tachiyomi.domain.category.interactor.GetCategories
|
||||
import tachiyomi.domain.category.model.Category
|
||||
import tachiyomi.domain.updates.service.UpdatesPreferences
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class UpdatesSettingsViewModel(
|
||||
val updatesPreferences: UpdatesPreferences = Injekt.get(),
|
||||
val getCategories: GetCategories = Injekt.get(),
|
||||
) : ViewModel() {
|
||||
|
||||
val includedCategories = updatesPreferences.filterIncludedCategories
|
||||
val excludedCategories = updatesPreferences.filterExcludedCategories
|
||||
|
||||
fun cycleCategory(category: Category) {
|
||||
when (category.id) {
|
||||
in includedCategories.get() -> {
|
||||
includedCategories.getAndSet { it - category.id }
|
||||
excludedCategories.getAndSet { it + category.id }
|
||||
}
|
||||
|
||||
in excludedCategories.get() -> excludedCategories.getAndSet { it - category.id }
|
||||
else -> includedCategories.getAndSet { it + category.id }
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleFilter(preference: (UpdatesPreferences) -> Preference<TriState>) {
|
||||
preference(updatesPreferences).getAndSet {
|
||||
it.next()
|
||||
|
||||
@@ -93,6 +93,8 @@ class UpdatesViewModel(
|
||||
started = it.filterStarted.toBooleanOrNull(),
|
||||
bookmarked = it.filterBookmarked.toBooleanOrNull(),
|
||||
hideExcludedScanlators = it.filterExcludedScanlators,
|
||||
includedCategories = it.filterIncludedCategories,
|
||||
excludedCategories = it.filterExcludedCategories,
|
||||
).distinctUntilChanged()
|
||||
},
|
||||
downloadCache.changes,
|
||||
@@ -423,13 +425,18 @@ class UpdatesViewModel(
|
||||
updatesPreferences.filterStarted.changes(),
|
||||
updatesPreferences.filterBookmarked.changes(),
|
||||
updatesPreferences.filterExcludedScanlators.changes(),
|
||||
) { downloaded, unread, started, bookmarked, excludedScanlators ->
|
||||
updatesPreferences.filterIncludedCategories.changes(),
|
||||
updatesPreferences.filterExcludedCategories.changes(),
|
||||
) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
ItemPreferences(
|
||||
filterDownloaded = downloaded,
|
||||
filterUnread = unread,
|
||||
filterStarted = started,
|
||||
filterBookmarked = bookmarked,
|
||||
filterExcludedScanlators = excludedScanlators,
|
||||
filterDownloaded = it[0] as TriState,
|
||||
filterUnread = it[1] as TriState,
|
||||
filterStarted = it[2] as TriState,
|
||||
filterBookmarked = it[3] as TriState,
|
||||
filterExcludedScanlators = it[4] as Boolean,
|
||||
filterIncludedCategories = it[5] as List<Long>,
|
||||
filterExcludedCategories = it[6] as List<Long>,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -445,6 +452,8 @@ class UpdatesViewModel(
|
||||
val filterStarted: TriState,
|
||||
val filterBookmarked: TriState,
|
||||
val filterExcludedScanlators: Boolean,
|
||||
val filterIncludedCategories: List<Long>,
|
||||
val filterExcludedCategories: List<Long>,
|
||||
)
|
||||
|
||||
@Immutable
|
||||
|
||||
Reference in New Issue
Block a user