Move SettingsItems composables to presentation-core

This commit is contained in:
arkon 2023-07-10 17:25:52 -04:00
parent efabe801be
commit 87bdee5990
16 changed files with 238 additions and 250 deletions

View file

@ -7,9 +7,9 @@ import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import tachiyomi.core.metadata.comicinfo.ComicInfo import tachiyomi.core.metadata.comicinfo.ComicInfo
import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus import tachiyomi.core.metadata.comicinfo.ComicInfoPublishingStatus
import tachiyomi.core.preference.TriState
import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.model.Chapter
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.TriStateFilter
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -20,19 +20,19 @@ val Manga.readingModeType: Long
val Manga.orientationType: Long val Manga.orientationType: Long
get() = viewerFlags and OrientationType.MASK.toLong() get() = viewerFlags and OrientationType.MASK.toLong()
val Manga.downloadedFilter: TriStateFilter val Manga.downloadedFilter: TriState
get() { get() {
if (forceDownloaded()) return TriStateFilter.ENABLED_IS if (forceDownloaded()) return TriState.ENABLED_IS
return when (downloadedFilterRaw) { return when (downloadedFilterRaw) {
Manga.CHAPTER_SHOW_DOWNLOADED -> TriStateFilter.ENABLED_IS Manga.CHAPTER_SHOW_DOWNLOADED -> TriState.ENABLED_IS
Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriStateFilter.ENABLED_NOT Manga.CHAPTER_SHOW_NOT_DOWNLOADED -> TriState.ENABLED_NOT
else -> TriStateFilter.DISABLED else -> TriState.DISABLED
} }
} }
fun Manga.chaptersFiltered(): Boolean { fun Manga.chaptersFiltered(): Boolean {
return unreadFilter != TriStateFilter.DISABLED || return unreadFilter != TriState.DISABLED ||
downloadedFilter != TriStateFilter.DISABLED || downloadedFilter != TriState.DISABLED ||
bookmarkedFilter != TriStateFilter.DISABLED bookmarkedFilter != TriState.DISABLED
} }
fun Manga.forceDownloaded(): Boolean { fun Manga.forceDownloaded(): Boolean {
return favorite && Injekt.get<BasePreferences>().downloadedOnly().get() return favorite && Injekt.get<BasePreferences>().downloadedOnly().get()

View file

@ -1,128 +0,0 @@
package eu.kanade.presentation.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.ContentAlpha
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.CheckBox
import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank
import androidx.compose.material.icons.rounded.DisabledByDefault
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import tachiyomi.domain.manga.model.TriStateFilter
import tachiyomi.presentation.core.components.SettingsItemsPaddings
@Composable
fun TriStateItem(
label: String,
state: TriStateFilter,
enabled: Boolean = true,
onClick: ((TriStateFilter) -> Unit)?,
) {
Row(
modifier = Modifier
.clickable(
enabled = enabled && onClick != null,
onClick = {
when (state) {
TriStateFilter.DISABLED -> onClick?.invoke(TriStateFilter.ENABLED_IS)
TriStateFilter.ENABLED_IS -> onClick?.invoke(TriStateFilter.ENABLED_NOT)
TriStateFilter.ENABLED_NOT -> onClick?.invoke(TriStateFilter.DISABLED)
}
},
)
.fillMaxWidth()
.padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
val stateAlpha = if (enabled && onClick != null) 1f else ContentAlpha.disabled
Icon(
imageVector = when (state) {
TriStateFilter.DISABLED -> Icons.Rounded.CheckBoxOutlineBlank
TriStateFilter.ENABLED_IS -> Icons.Rounded.CheckBox
TriStateFilter.ENABLED_NOT -> Icons.Rounded.DisabledByDefault
},
contentDescription = null,
tint = if (!enabled || state == TriStateFilter.DISABLED) {
MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = stateAlpha)
} else {
when (onClick) {
null -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled)
else -> MaterialTheme.colorScheme.primary
}
},
)
Text(
text = label,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = stateAlpha),
style = MaterialTheme.typography.bodyMedium,
)
}
}
@Composable
fun SelectItem(
label: String,
options: Array<out Any?>,
selectedIndex: Int,
onSelect: (Int) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded },
) {
OutlinedTextField(
modifier = Modifier
.menuAnchor()
.fillMaxWidth()
.padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical),
label = { Text(text = label) },
value = options[selectedIndex].toString(),
onValueChange = {},
readOnly = true,
singleLine = true,
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(
expanded = expanded,
)
},
colors = ExposedDropdownMenuDefaults.textFieldColors(),
)
ExposedDropdownMenu(
modifier = Modifier.exposedDropdownSize(matchTextFieldWidth = true),
expanded = expanded,
onDismissRequest = { expanded = false },
) {
options.forEachIndexed { index, text ->
DropdownMenuItem(
text = { Text(text.toString()) },
onClick = {
onSelect(index)
expanded = false
},
)
}
}
}
}

View file

@ -14,21 +14,21 @@ import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialog
import eu.kanade.presentation.components.TabbedDialogPaddings import eu.kanade.presentation.components.TabbedDialogPaddings
import eu.kanade.presentation.components.TriStateItem
import eu.kanade.presentation.util.collectAsState import eu.kanade.presentation.util.collectAsState
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.ui.library.LibrarySettingsScreenModel import eu.kanade.tachiyomi.ui.library.LibrarySettingsScreenModel
import tachiyomi.core.preference.TriState
import tachiyomi.domain.category.model.Category import tachiyomi.domain.category.model.Category
import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.LibrarySort import tachiyomi.domain.library.model.LibrarySort
import tachiyomi.domain.library.model.sort import tachiyomi.domain.library.model.sort
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.model.TriStateFilter
import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.HeadingItem import tachiyomi.presentation.core.components.HeadingItem
import tachiyomi.presentation.core.components.RadioItem import tachiyomi.presentation.core.components.RadioItem
import tachiyomi.presentation.core.components.SliderItem import tachiyomi.presentation.core.components.SliderItem
import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.SortItem
import tachiyomi.presentation.core.components.TriStateItem
@Composable @Composable
fun LibrarySettingsDialog( fun LibrarySettingsDialog(
@ -74,7 +74,7 @@ private fun ColumnScope.FilterPage(
TriStateItem( TriStateItem(
label = stringResource(R.string.label_downloaded), label = stringResource(R.string.label_downloaded),
state = if (downloadedOnly) { state = if (downloadedOnly) {
TriStateFilter.ENABLED_IS TriState.ENABLED_IS
} else { } else {
filterDownloaded filterDownloaded
}, },

View file

@ -27,20 +27,20 @@ import eu.kanade.domain.manga.model.downloadedFilter
import eu.kanade.domain.manga.model.forceDownloaded import eu.kanade.domain.manga.model.forceDownloaded
import eu.kanade.presentation.components.TabbedDialog import eu.kanade.presentation.components.TabbedDialog
import eu.kanade.presentation.components.TabbedDialogPaddings import eu.kanade.presentation.components.TabbedDialogPaddings
import eu.kanade.presentation.components.TriStateItem
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import tachiyomi.core.preference.TriState
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.TriStateFilter
import tachiyomi.presentation.core.components.RadioItem import tachiyomi.presentation.core.components.RadioItem
import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.SortItem
import tachiyomi.presentation.core.components.TriStateItem
@Composable @Composable
fun ChapterSettingsDialog( fun ChapterSettingsDialog(
onDismissRequest: () -> Unit, onDismissRequest: () -> Unit,
manga: Manga? = null, manga: Manga? = null,
onDownloadFilterChanged: (TriStateFilter) -> Unit, onDownloadFilterChanged: (TriState) -> Unit,
onUnreadFilterChanged: (TriStateFilter) -> Unit, onUnreadFilterChanged: (TriState) -> Unit,
onBookmarkedFilterChanged: (TriStateFilter) -> Unit, onBookmarkedFilterChanged: (TriState) -> Unit,
onSortModeChanged: (Long) -> Unit, onSortModeChanged: (Long) -> Unit,
onDisplayModeChanged: (Long) -> Unit, onDisplayModeChanged: (Long) -> Unit,
onSetAsDefault: (applyToExistingManga: Boolean) -> Unit, onSetAsDefault: (applyToExistingManga: Boolean) -> Unit,
@ -78,11 +78,11 @@ fun ChapterSettingsDialog(
when (page) { when (page) {
0 -> { 0 -> {
FilterPage( FilterPage(
downloadFilter = manga?.downloadedFilter ?: TriStateFilter.DISABLED, downloadFilter = manga?.downloadedFilter ?: TriState.DISABLED,
onDownloadFilterChanged = onDownloadFilterChanged.takeUnless { manga?.forceDownloaded() == true }, onDownloadFilterChanged = onDownloadFilterChanged.takeUnless { manga?.forceDownloaded() == true },
unreadFilter = manga?.unreadFilter ?: TriStateFilter.DISABLED, unreadFilter = manga?.unreadFilter ?: TriState.DISABLED,
onUnreadFilterChanged = onUnreadFilterChanged, onUnreadFilterChanged = onUnreadFilterChanged,
bookmarkedFilter = manga?.bookmarkedFilter ?: TriStateFilter.DISABLED, bookmarkedFilter = manga?.bookmarkedFilter ?: TriState.DISABLED,
onBookmarkedFilterChanged = onBookmarkedFilterChanged, onBookmarkedFilterChanged = onBookmarkedFilterChanged,
) )
} }
@ -106,12 +106,12 @@ fun ChapterSettingsDialog(
@Composable @Composable
private fun ColumnScope.FilterPage( private fun ColumnScope.FilterPage(
downloadFilter: TriStateFilter, downloadFilter: TriState,
onDownloadFilterChanged: ((TriStateFilter) -> Unit)?, onDownloadFilterChanged: ((TriState) -> Unit)?,
unreadFilter: TriStateFilter, unreadFilter: TriState,
onUnreadFilterChanged: (TriStateFilter) -> Unit, onUnreadFilterChanged: (TriState) -> Unit,
bookmarkedFilter: TriStateFilter, bookmarkedFilter: TriState,
onBookmarkedFilterChanged: (TriStateFilter) -> Unit, onBookmarkedFilterChanged: (TriState) -> Unit,
) { ) {
TriStateItem( TriStateItem(
label = stringResource(R.string.label_downloaded), label = stringResource(R.string.label_downloaded),

View file

@ -21,12 +21,11 @@ import eu.kanade.tachiyomi.util.system.isReleaseBuildType
import eu.kanade.tachiyomi.util.system.toast import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.workManager import eu.kanade.tachiyomi.util.system.workManager
import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.preference.TriState
import tachiyomi.core.preference.getEnum import tachiyomi.core.preference.getEnum
import tachiyomi.domain.backup.service.BackupPreferences import tachiyomi.domain.backup.service.BackupPreferences
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED import tachiyomi.domain.library.service.LibraryPreferences.Companion.MANGA_NON_COMPLETED
import tachiyomi.domain.manga.model.TriStateFilter
import uy.kohesive.injekt.api.get
import java.io.File import java.io.File
object Migrations { object Migrations {
@ -350,12 +349,12 @@ object Migrations {
remove(key) remove(key)
val newValue = when (pref.get()) { val newValue = when (pref.get()) {
1 -> TriStateFilter.ENABLED_IS 1 -> TriState.ENABLED_IS
2 -> TriStateFilter.ENABLED_NOT 2 -> TriState.ENABLED_NOT
else -> TriStateFilter.DISABLED else -> TriState.DISABLED
} }
preferenceStore.getEnum("${key}_v2", TriStateFilter.DISABLED).set(newValue) preferenceStore.getEnum("${key}_v2", TriState.DISABLED).set(newValue)
} }
} }
} }

View file

@ -16,17 +16,17 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import eu.kanade.presentation.components.AdaptiveSheet import eu.kanade.presentation.components.AdaptiveSheet
import eu.kanade.presentation.components.SelectItem
import eu.kanade.presentation.components.TriStateItem
import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.R
import eu.kanade.tachiyomi.source.model.Filter import eu.kanade.tachiyomi.source.model.Filter
import eu.kanade.tachiyomi.source.model.FilterList import eu.kanade.tachiyomi.source.model.FilterList
import tachiyomi.domain.manga.model.TriStateFilter import tachiyomi.core.preference.TriState
import tachiyomi.presentation.core.components.CheckboxItem import tachiyomi.presentation.core.components.CheckboxItem
import tachiyomi.presentation.core.components.CollapsibleBox import tachiyomi.presentation.core.components.CollapsibleBox
import tachiyomi.presentation.core.components.HeadingItem import tachiyomi.presentation.core.components.HeadingItem
import tachiyomi.presentation.core.components.SelectItem
import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.SortItem
import tachiyomi.presentation.core.components.TextItem import tachiyomi.presentation.core.components.TextItem
import tachiyomi.presentation.core.components.TriStateItem
import tachiyomi.presentation.core.components.material.Button import tachiyomi.presentation.core.components.material.Button
import tachiyomi.presentation.core.components.material.Divider import tachiyomi.presentation.core.components.material.Divider
@ -164,19 +164,19 @@ private fun FilterItem(filter: Filter<*>, onUpdate: () -> Unit) {
} }
} }
private fun Int.toTriStateFilter(): TriStateFilter { private fun Int.toTriStateFilter(): TriState {
return when (this) { return when (this) {
Filter.TriState.STATE_IGNORE -> TriStateFilter.DISABLED Filter.TriState.STATE_IGNORE -> TriState.DISABLED
Filter.TriState.STATE_INCLUDE -> TriStateFilter.ENABLED_IS Filter.TriState.STATE_INCLUDE -> TriState.ENABLED_IS
Filter.TriState.STATE_EXCLUDE -> TriStateFilter.ENABLED_NOT Filter.TriState.STATE_EXCLUDE -> TriState.ENABLED_NOT
else -> throw IllegalStateException("Unknown TriState state: $this") else -> throw IllegalStateException("Unknown TriState state: $this")
} }
} }
private fun TriStateFilter.toTriStateInt(): Int { private fun TriState.toTriStateInt(): Int {
return when (this) { return when (this) {
TriStateFilter.DISABLED -> Filter.TriState.STATE_IGNORE TriState.DISABLED -> Filter.TriState.STATE_IGNORE
TriStateFilter.ENABLED_IS -> Filter.TriState.STATE_INCLUDE TriState.ENABLED_IS -> Filter.TriState.STATE_INCLUDE
TriStateFilter.ENABLED_NOT -> Filter.TriState.STATE_EXCLUDE TriState.ENABLED_NOT -> Filter.TriState.STATE_EXCLUDE
} }
} }

View file

@ -40,6 +40,7 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import tachiyomi.core.preference.CheckboxState import tachiyomi.core.preference.CheckboxState
import tachiyomi.core.preference.TriState
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.launchNonCancellable import tachiyomi.core.util.lang.launchNonCancellable
import tachiyomi.core.util.lang.withIOContext import tachiyomi.core.util.lang.withIOContext
@ -57,7 +58,6 @@ import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.interactor.GetLibraryManga import tachiyomi.domain.manga.interactor.GetLibraryManga
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.MangaUpdate import tachiyomi.domain.manga.model.MangaUpdate
import tachiyomi.domain.manga.model.TriStateFilter
import tachiyomi.domain.manga.model.applyFilter import tachiyomi.domain.manga.model.applyFilter
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import tachiyomi.domain.track.interactor.GetTracksPerManga import tachiyomi.domain.track.interactor.GetTracksPerManga
@ -153,7 +153,7 @@ class LibraryScreenModel(
prefs.filterBookmarked, prefs.filterBookmarked,
prefs.filterCompleted, prefs.filterCompleted,
) + trackFilter.values ) + trackFilter.values
).any { it != TriStateFilter.DISABLED } ).any { it != TriState.DISABLED }
} }
.distinctUntilChanged() .distinctUntilChanged()
.onEach { .onEach {
@ -169,12 +169,12 @@ class LibraryScreenModel(
*/ */
private suspend fun LibraryMap.applyFilters( private suspend fun LibraryMap.applyFilters(
trackMap: Map<Long, List<Long>>, trackMap: Map<Long, List<Long>>,
loggedInTrackServices: Map<Long, TriStateFilter>, loggedInTrackServices: Map<Long, TriState>,
): LibraryMap { ): LibraryMap {
val prefs = getLibraryItemPreferencesFlow().first() val prefs = getLibraryItemPreferencesFlow().first()
val downloadedOnly = prefs.globalFilterDownloaded val downloadedOnly = prefs.globalFilterDownloaded
val filterDownloaded = val filterDownloaded =
if (downloadedOnly) TriStateFilter.ENABLED_IS else prefs.filterDownloaded if (downloadedOnly) TriState.ENABLED_IS else prefs.filterDownloaded
val filterUnread = prefs.filterUnread val filterUnread = prefs.filterUnread
val filterStarted = prefs.filterStarted val filterStarted = prefs.filterStarted
val filterBookmarked = prefs.filterBookmarked val filterBookmarked = prefs.filterBookmarked
@ -182,8 +182,8 @@ class LibraryScreenModel(
val isNotLoggedInAnyTrack = loggedInTrackServices.isEmpty() val isNotLoggedInAnyTrack = loggedInTrackServices.isEmpty()
val excludedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriStateFilter.ENABLED_NOT) it.key else null } val excludedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriState.ENABLED_NOT) it.key else null }
val includedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriStateFilter.ENABLED_IS) it.key else null } val includedTracks = loggedInTrackServices.mapNotNull { if (it.value == TriState.ENABLED_IS) it.key else null }
val trackFiltersIsIgnored = includedTracks.isEmpty() && excludedTracks.isEmpty() val trackFiltersIsIgnored = includedTracks.isEmpty() && excludedTracks.isEmpty()
val filterFnDownloaded: (LibraryItem) -> Boolean = { val filterFnDownloaded: (LibraryItem) -> Boolean = {
@ -308,11 +308,11 @@ class LibraryScreenModel(
localBadge = it[1] as Boolean, localBadge = it[1] as Boolean,
languageBadge = it[2] as Boolean, languageBadge = it[2] as Boolean,
globalFilterDownloaded = it[3] as Boolean, globalFilterDownloaded = it[3] as Boolean,
filterDownloaded = it[4] as TriStateFilter, filterDownloaded = it[4] as TriState,
filterUnread = it[5] as TriStateFilter, filterUnread = it[5] as TriState,
filterStarted = it[6] as TriStateFilter, filterStarted = it[6] as TriState,
filterBookmarked = it[7] as TriStateFilter, filterBookmarked = it[7] as TriState,
filterCompleted = it[8] as TriStateFilter, filterCompleted = it[8] as TriState,
) )
}, },
) )
@ -365,7 +365,7 @@ class LibraryScreenModel(
* *
* @return map of track id with the filter value * @return map of track id with the filter value
*/ */
private fun getTrackingFilterFlow(): Flow<Map<Long, TriStateFilter>> { private fun getTrackingFilterFlow(): Flow<Map<Long, TriState>> {
val loggedServices = trackManager.services.filter { it.isLogged } val loggedServices = trackManager.services.filter { it.isLogged }
return if (loggedServices.isNotEmpty()) { return if (loggedServices.isNotEmpty()) {
val prefFlows = loggedServices val prefFlows = loggedServices
@ -670,11 +670,11 @@ class LibraryScreenModel(
val languageBadge: Boolean, val languageBadge: Boolean,
val globalFilterDownloaded: Boolean, val globalFilterDownloaded: Boolean,
val filterDownloaded: TriStateFilter, val filterDownloaded: TriState,
val filterUnread: TriStateFilter, val filterUnread: TriState,
val filterStarted: TriStateFilter, val filterStarted: TriState,
val filterBookmarked: TriStateFilter, val filterBookmarked: TriState,
val filterCompleted: TriStateFilter, val filterCompleted: TriState,
) )
@Immutable @Immutable

View file

@ -6,6 +6,7 @@ import eu.kanade.domain.base.BasePreferences
import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackManager
import eu.kanade.tachiyomi.util.preference.toggle import eu.kanade.tachiyomi.util.preference.toggle
import tachiyomi.core.preference.Preference import tachiyomi.core.preference.Preference
import tachiyomi.core.preference.TriState
import tachiyomi.core.preference.getAndSet import tachiyomi.core.preference.getAndSet
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
import tachiyomi.domain.category.interactor.SetDisplayMode import tachiyomi.domain.category.interactor.SetDisplayMode
@ -14,7 +15,6 @@ import tachiyomi.domain.category.model.Category
import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.LibrarySort import tachiyomi.domain.library.model.LibrarySort
import tachiyomi.domain.library.service.LibraryPreferences import tachiyomi.domain.library.service.LibraryPreferences
import tachiyomi.domain.manga.model.TriStateFilter
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
@ -33,7 +33,7 @@ class LibrarySettingsScreenModel(
preference(libraryPreferences).toggle() preference(libraryPreferences).toggle()
} }
fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriStateFilter>) { fun toggleFilter(preference: (LibraryPreferences) -> Preference<TriState>) {
preference(libraryPreferences).getAndSet { preference(libraryPreferences).getAndSet {
it.next() it.next()
} }

View file

@ -46,6 +46,7 @@ import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.preference.CheckboxState import tachiyomi.core.preference.CheckboxState
import tachiyomi.core.preference.TriState
import tachiyomi.core.preference.mapAsCheckboxState import tachiyomi.core.preference.mapAsCheckboxState
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
import tachiyomi.core.util.lang.launchNonCancellable import tachiyomi.core.util.lang.launchNonCancellable
@ -67,7 +68,6 @@ import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
import tachiyomi.domain.manga.interactor.GetMangaWithChapters import tachiyomi.domain.manga.interactor.GetMangaWithChapters
import tachiyomi.domain.manga.interactor.SetMangaChapterFlags import tachiyomi.domain.manga.interactor.SetMangaChapterFlags
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.TriStateFilter
import tachiyomi.domain.manga.model.applyFilter import tachiyomi.domain.manga.model.applyFilter
import tachiyomi.domain.source.service.SourceManager import tachiyomi.domain.source.service.SourceManager
import tachiyomi.domain.track.interactor.GetTracks import tachiyomi.domain.track.interactor.GetTracks
@ -743,13 +743,13 @@ class MangaInfoScreenModel(
* Sets the read filter and requests an UI update. * Sets the read filter and requests an UI update.
* @param state whether to display only unread chapters or all chapters. * @param state whether to display only unread chapters or all chapters.
*/ */
fun setUnreadFilter(state: TriStateFilter) { fun setUnreadFilter(state: TriState) {
val manga = successState?.manga ?: return val manga = successState?.manga ?: return
val flag = when (state) { val flag = when (state) {
TriStateFilter.DISABLED -> Manga.SHOW_ALL TriState.DISABLED -> Manga.SHOW_ALL
TriStateFilter.ENABLED_IS -> Manga.CHAPTER_SHOW_UNREAD TriState.ENABLED_IS -> Manga.CHAPTER_SHOW_UNREAD
TriStateFilter.ENABLED_NOT -> Manga.CHAPTER_SHOW_READ TriState.ENABLED_NOT -> Manga.CHAPTER_SHOW_READ
} }
coroutineScope.launchNonCancellable { coroutineScope.launchNonCancellable {
setMangaChapterFlags.awaitSetUnreadFilter(manga, flag) setMangaChapterFlags.awaitSetUnreadFilter(manga, flag)
@ -760,13 +760,13 @@ class MangaInfoScreenModel(
* Sets the download filter and requests an UI update. * Sets the download filter and requests an UI update.
* @param state whether to display only downloaded chapters or all chapters. * @param state whether to display only downloaded chapters or all chapters.
*/ */
fun setDownloadedFilter(state: TriStateFilter) { fun setDownloadedFilter(state: TriState) {
val manga = successState?.manga ?: return val manga = successState?.manga ?: return
val flag = when (state) { val flag = when (state) {
TriStateFilter.DISABLED -> Manga.SHOW_ALL TriState.DISABLED -> Manga.SHOW_ALL
TriStateFilter.ENABLED_IS -> Manga.CHAPTER_SHOW_DOWNLOADED TriState.ENABLED_IS -> Manga.CHAPTER_SHOW_DOWNLOADED
TriStateFilter.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_DOWNLOADED TriState.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_DOWNLOADED
} }
coroutineScope.launchNonCancellable { coroutineScope.launchNonCancellable {
@ -778,13 +778,13 @@ class MangaInfoScreenModel(
* Sets the bookmark filter and requests an UI update. * Sets the bookmark filter and requests an UI update.
* @param state whether to display only bookmarked chapters or all chapters. * @param state whether to display only bookmarked chapters or all chapters.
*/ */
fun setBookmarkedFilter(state: TriStateFilter) { fun setBookmarkedFilter(state: TriState) {
val manga = successState?.manga ?: return val manga = successState?.manga ?: return
val flag = when (state) { val flag = when (state) {
TriStateFilter.DISABLED -> Manga.SHOW_ALL TriState.DISABLED -> Manga.SHOW_ALL
TriStateFilter.ENABLED_IS -> Manga.CHAPTER_SHOW_BOOKMARKED TriState.ENABLED_IS -> Manga.CHAPTER_SHOW_BOOKMARKED
TriStateFilter.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_BOOKMARKED TriState.ENABLED_NOT -> Manga.CHAPTER_SHOW_NOT_BOOKMARKED
} }
coroutineScope.launchNonCancellable { coroutineScope.launchNonCancellable {

View file

@ -0,0 +1,16 @@
package tachiyomi.core.preference
enum class TriState {
DISABLED, // Disable filter
ENABLED_IS, // Enabled with "is" filter
ENABLED_NOT, // Enabled with "not" filter
;
fun next(): TriState {
return when (this) {
DISABLED -> ENABLED_IS
ENABLED_IS -> ENABLED_NOT
ENABLED_NOT -> DISABLED
}
}
}

View file

@ -1,11 +1,11 @@
package tachiyomi.domain.library.service package tachiyomi.domain.library.service
import tachiyomi.core.preference.PreferenceStore import tachiyomi.core.preference.PreferenceStore
import tachiyomi.core.preference.TriState
import tachiyomi.core.preference.getEnum import tachiyomi.core.preference.getEnum
import tachiyomi.domain.library.model.LibraryDisplayMode import tachiyomi.domain.library.model.LibraryDisplayMode
import tachiyomi.domain.library.model.LibrarySort import tachiyomi.domain.library.model.LibrarySort
import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.manga.model.Manga
import tachiyomi.domain.manga.model.TriStateFilter
class LibraryPreferences( class LibraryPreferences(
private val preferenceStore: PreferenceStore, private val preferenceStore: PreferenceStore,
@ -49,27 +49,27 @@ class LibraryPreferences(
// region Filter // region Filter
fun filterDownloaded() = preferenceStore.getEnum("pref_filter_library_downloaded_v2", TriStateFilter.DISABLED) fun filterDownloaded() = preferenceStore.getEnum("pref_filter_library_downloaded_v2", TriState.DISABLED)
fun filterUnread() = preferenceStore.getEnum("pref_filter_library_unread_v2", TriStateFilter.DISABLED) fun filterUnread() = preferenceStore.getEnum("pref_filter_library_unread_v2", TriState.DISABLED)
fun filterStarted() = preferenceStore.getEnum("pref_filter_library_started_v2", TriStateFilter.DISABLED) fun filterStarted() = preferenceStore.getEnum("pref_filter_library_started_v2", TriState.DISABLED)
fun filterBookmarked() = preferenceStore.getEnum("pref_filter_library_bookmarked_v2", TriStateFilter.DISABLED) fun filterBookmarked() = preferenceStore.getEnum("pref_filter_library_bookmarked_v2", TriState.DISABLED)
fun filterCompleted() = preferenceStore.getEnum("pref_filter_library_completed_v2", TriStateFilter.DISABLED) fun filterCompleted() = preferenceStore.getEnum("pref_filter_library_completed_v2", TriState.DISABLED)
fun filterIntervalCustom() = preferenceStore.getEnum("pref_filter_library_interval_custom", TriStateFilter.DISABLED) fun filterIntervalCustom() = preferenceStore.getEnum("pref_filter_library_interval_custom", TriState.DISABLED)
fun filterIntervalLong() = preferenceStore.getEnum("pref_filter_library_interval_long", TriStateFilter.DISABLED) fun filterIntervalLong() = preferenceStore.getEnum("pref_filter_library_interval_long", TriState.DISABLED)
fun filterIntervalLate() = preferenceStore.getEnum("pref_filter_library_interval_late", TriStateFilter.DISABLED) fun filterIntervalLate() = preferenceStore.getEnum("pref_filter_library_interval_late", TriState.DISABLED)
fun filterIntervalDropped() = preferenceStore.getEnum("pref_filter_library_interval_dropped", TriStateFilter.DISABLED) fun filterIntervalDropped() = preferenceStore.getEnum("pref_filter_library_interval_dropped", TriState.DISABLED)
fun filterIntervalPassed() = preferenceStore.getEnum("pref_filter_library_interval_passed", TriStateFilter.DISABLED) fun filterIntervalPassed() = preferenceStore.getEnum("pref_filter_library_interval_passed", TriState.DISABLED)
fun filterTracking(id: Int) = preferenceStore.getEnum("pref_filter_library_tracked_${id}_v2", TriStateFilter.DISABLED) fun filterTracking(id: Int) = preferenceStore.getEnum("pref_filter_library_tracked_${id}_v2", TriState.DISABLED)
// endregion // endregion

View file

@ -1,6 +1,7 @@
package tachiyomi.domain.manga.model package tachiyomi.domain.manga.model
import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.source.model.UpdateStrategy
import tachiyomi.core.preference.TriState
import java.io.Serializable import java.io.Serializable
data class Manga( data class Manga(
@ -43,18 +44,18 @@ data class Manga(
val bookmarkedFilterRaw: Long val bookmarkedFilterRaw: Long
get() = chapterFlags and CHAPTER_BOOKMARKED_MASK get() = chapterFlags and CHAPTER_BOOKMARKED_MASK
val unreadFilter: TriStateFilter val unreadFilter: TriState
get() = when (unreadFilterRaw) { get() = when (unreadFilterRaw) {
CHAPTER_SHOW_UNREAD -> TriStateFilter.ENABLED_IS CHAPTER_SHOW_UNREAD -> TriState.ENABLED_IS
CHAPTER_SHOW_READ -> TriStateFilter.ENABLED_NOT CHAPTER_SHOW_READ -> TriState.ENABLED_NOT
else -> TriStateFilter.DISABLED else -> TriState.DISABLED
} }
val bookmarkedFilter: TriStateFilter val bookmarkedFilter: TriState
get() = when (bookmarkedFilterRaw) { get() = when (bookmarkedFilterRaw) {
CHAPTER_SHOW_BOOKMARKED -> TriStateFilter.ENABLED_IS CHAPTER_SHOW_BOOKMARKED -> TriState.ENABLED_IS
CHAPTER_SHOW_NOT_BOOKMARKED -> TriStateFilter.ENABLED_NOT CHAPTER_SHOW_NOT_BOOKMARKED -> TriState.ENABLED_NOT
else -> TriStateFilter.DISABLED else -> TriState.DISABLED
} }
fun sortDescending(): Boolean { fun sortDescending(): Boolean {

View file

@ -0,0 +1,9 @@
package tachiyomi.domain.manga.model
import tachiyomi.core.preference.TriState
inline fun applyFilter(filter: TriState, predicate: () -> Boolean): Boolean = when (filter) {
TriState.DISABLED -> true
TriState.ENABLED_IS -> predicate()
TriState.ENABLED_NOT -> !predicate()
}

View file

@ -1,22 +0,0 @@
package tachiyomi.domain.manga.model
enum class TriStateFilter {
DISABLED, // Disable filter
ENABLED_IS, // Enabled with "is" filter
ENABLED_NOT, // Enabled with "not" filter
;
fun next(): TriStateFilter {
return when (this) {
DISABLED -> ENABLED_IS
ENABLED_IS -> ENABLED_NOT
ENABLED_NOT -> DISABLED
}
}
}
inline fun applyFilter(filter: TriStateFilter, predicate: () -> Boolean): Boolean = when (filter) {
TriStateFilter.DISABLED -> true
TriStateFilter.ENABLED_IS -> predicate()
TriStateFilter.ENABLED_NOT -> !predicate()
}

View file

@ -21,6 +21,8 @@ android {
} }
dependencies { dependencies {
implementation(project(":core"))
// Compose // Compose
implementation(platform(compose.bom)) implementation(platform(compose.bom))
implementation(compose.activity) implementation(compose.activity)

View file

@ -10,10 +10,17 @@ import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.ContentAlpha
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDownward import androidx.compose.material.icons.filled.ArrowDownward
import androidx.compose.material.icons.filled.ArrowUpward import androidx.compose.material.icons.filled.ArrowUpward
import androidx.compose.material.icons.rounded.CheckBox
import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank
import androidx.compose.material.icons.rounded.DisabledByDefault
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
@ -21,11 +28,16 @@ import androidx.compose.material3.RadioButton
import androidx.compose.material3.Slider import androidx.compose.material3.Slider
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import tachiyomi.core.preference.TriState
import tachiyomi.presentation.core.theme.header import tachiyomi.presentation.core.theme.header
object SettingsItemsPaddings { object SettingsItemsPaddings {
@ -175,22 +187,99 @@ fun SliderItem(
} }
@Composable @Composable
private fun BaseSettingsItem( fun SelectItem(
label: String, label: String,
widget: @Composable RowScope.() -> Unit, options: Array<out Any?>,
onClick: () -> Unit, selectedIndex: Int,
onSelect: (Int) -> Unit,
) {
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = !expanded },
) {
OutlinedTextField(
modifier = Modifier
.menuAnchor()
.fillMaxWidth()
.padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical),
label = { Text(text = label) },
value = options[selectedIndex].toString(),
onValueChange = {},
readOnly = true,
singleLine = true,
trailingIcon = {
ExposedDropdownMenuDefaults.TrailingIcon(
expanded = expanded,
)
},
colors = ExposedDropdownMenuDefaults.textFieldColors(),
)
ExposedDropdownMenu(
modifier = Modifier.exposedDropdownSize(matchTextFieldWidth = true),
expanded = expanded,
onDismissRequest = { expanded = false },
) {
options.forEachIndexed { index, text ->
DropdownMenuItem(
text = { Text(text.toString()) },
onClick = {
onSelect(index)
expanded = false
},
)
}
}
}
}
@Composable
fun TriStateItem(
label: String,
state: TriState,
enabled: Boolean = true,
onClick: ((TriState) -> Unit)?,
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
.clickable(onClick = onClick) .clickable(
enabled = enabled && onClick != null,
onClick = {
when (state) {
TriState.DISABLED -> onClick?.invoke(TriState.ENABLED_IS)
TriState.ENABLED_IS -> onClick?.invoke(TriState.ENABLED_NOT)
TriState.ENABLED_NOT -> onClick?.invoke(TriState.DISABLED)
}
},
)
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical), .padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp), horizontalArrangement = Arrangement.spacedBy(24.dp),
) { ) {
widget(this) val stateAlpha = if (enabled && onClick != null) 1f else ContentAlpha.disabled
Icon(
imageVector = when (state) {
TriState.DISABLED -> Icons.Rounded.CheckBoxOutlineBlank
TriState.ENABLED_IS -> Icons.Rounded.CheckBox
TriState.ENABLED_NOT -> Icons.Rounded.DisabledByDefault
},
contentDescription = null,
tint = if (!enabled || state == TriState.DISABLED) {
MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = stateAlpha)
} else {
when (onClick) {
null -> MaterialTheme.colorScheme.onSurface.copy(alpha = ContentAlpha.disabled)
else -> MaterialTheme.colorScheme.primary
}
},
)
Text( Text(
text = label, text = label,
color = MaterialTheme.colorScheme.onSurface.copy(alpha = stateAlpha),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
) )
} }
@ -212,3 +301,25 @@ fun TextItem(
singleLine = true, singleLine = true,
) )
} }
@Composable
private fun BaseSettingsItem(
label: String,
widget: @Composable RowScope.() -> Unit,
onClick: () -> Unit,
) {
Row(
modifier = Modifier
.clickable(onClick = onClick)
.fillMaxWidth()
.padding(horizontal = SettingsItemsPaddings.Horizontal, vertical = SettingsItemsPaddings.Vertical),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(24.dp),
) {
widget(this)
Text(
text = label,
style = MaterialTheme.typography.bodyMedium,
)
}
}