feat(history): Add Filters to History screen (#1433)
* Add Filters to History screen * Revert favorite condition
This commit is contained in:
parent
3e7dc5c41a
commit
f373818d93
18 changed files with 368 additions and 68 deletions
|
|
@ -5,12 +5,12 @@ import androidx.compose.foundation.layout.padding
|
|||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.DeleteSweep
|
||||
import androidx.compose.material.icons.outlined.Panorama
|
||||
import androidx.compose.material.icons.outlined.FilterList
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
|
|
@ -23,19 +23,16 @@ import eu.kanade.presentation.history.components.HistoryItem
|
|||
import eu.kanade.presentation.theme.TachiyomiPreviewTheme
|
||||
import eu.kanade.presentation.util.animateItemFastScroll
|
||||
import eu.kanade.tachiyomi.ui.history.HistoryScreenModel
|
||||
import eu.kanade.tachiyomi.ui.updates.UpdatesSettingsScreenModel
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||
import tachiyomi.domain.updates.service.UpdatesPreferences
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.i18n.kmk.KMR
|
||||
import tachiyomi.presentation.core.components.FastScrollLazyColumn
|
||||
import tachiyomi.presentation.core.components.ListGroupHeader
|
||||
import tachiyomi.presentation.core.components.material.Scaffold
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.screens.EmptyScreen
|
||||
import tachiyomi.presentation.core.screens.LoadingScreen
|
||||
import tachiyomi.presentation.core.util.collectAsState
|
||||
import tachiyomi.presentation.core.theme.active
|
||||
import java.time.LocalDate
|
||||
|
||||
@Composable
|
||||
|
|
@ -48,13 +45,11 @@ fun HistoryScreen(
|
|||
onClickFavorite: (mangaId: Long) -> Unit,
|
||||
onDialogChange: (HistoryScreenModel.Dialog?) -> Unit,
|
||||
// KMK -->
|
||||
settingsScreenModel: UpdatesSettingsScreenModel,
|
||||
onFilterClicked: () -> Unit,
|
||||
hasActiveFilters: Boolean,
|
||||
usePanoramaCover: Boolean,
|
||||
// KMK <--
|
||||
) {
|
||||
// KMK -->
|
||||
val usePanoramaCover by settingsScreenModel.updatesPreferences.usePanoramaCover().collectAsState()
|
||||
// KMK <--
|
||||
|
||||
Scaffold(
|
||||
topBar = { scrollBehavior ->
|
||||
SearchToolbar(
|
||||
|
|
@ -63,33 +58,23 @@ fun HistoryScreen(
|
|||
onChangeSearchQuery = onSearchQueryChange,
|
||||
actions = {
|
||||
AppBarActions(
|
||||
// KMK -->
|
||||
persistentListOf<AppBar.AppBarAction>().builder()
|
||||
.apply {
|
||||
if (!state.list.isNullOrEmpty()) {
|
||||
add(
|
||||
AppBar.Action(
|
||||
title = stringResource(KMR.strings.action_panorama_cover),
|
||||
icon = Icons.Outlined.Panorama,
|
||||
iconTint = MaterialTheme.colorScheme.primary.takeIf { usePanoramaCover },
|
||||
onClick = { settingsScreenModel.toggleSwitch(UpdatesPreferences::usePanoramaCover) },
|
||||
),
|
||||
)
|
||||
}
|
||||
add(
|
||||
// KMK <--
|
||||
AppBar.Action(
|
||||
title = stringResource(MR.strings.pref_clear_history),
|
||||
icon = Icons.Outlined.DeleteSweep,
|
||||
onClick = {
|
||||
onDialogChange(HistoryScreenModel.Dialog.DeleteAll)
|
||||
},
|
||||
),
|
||||
// KMK -->
|
||||
)
|
||||
}
|
||||
.build(),
|
||||
// KMK <--
|
||||
persistentListOf(
|
||||
// KMK -->
|
||||
AppBar.Action(
|
||||
title = stringResource(MR.strings.action_filter),
|
||||
icon = Icons.Outlined.FilterList,
|
||||
iconTint = if (hasActiveFilters) MaterialTheme.colorScheme.active else LocalContentColor.current,
|
||||
onClick = onFilterClicked,
|
||||
),
|
||||
// KMK <--
|
||||
AppBar.Action(
|
||||
title = stringResource(MR.strings.pref_clear_history),
|
||||
icon = Icons.Outlined.DeleteSweep,
|
||||
onClick = {
|
||||
onDialogChange(HistoryScreenModel.Dialog.DeleteAll)
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
},
|
||||
scrollBehavior = scrollBehavior,
|
||||
|
|
@ -198,7 +183,6 @@ internal fun HistoryScreenPreviews(
|
|||
@PreviewParameter(HistoryScreenModelStateProvider::class)
|
||||
historyState: HistoryScreenModel.State,
|
||||
) {
|
||||
val settingsScreenModel = UpdatesSettingsScreenModel()
|
||||
TachiyomiPreviewTheme {
|
||||
HistoryScreen(
|
||||
state = historyState,
|
||||
|
|
@ -208,7 +192,11 @@ internal fun HistoryScreenPreviews(
|
|||
onClickResume = { _, _ -> run {} },
|
||||
onDialogChange = {},
|
||||
onClickFavorite = {},
|
||||
settingsScreenModel = settingsScreenModel,
|
||||
// KMK -->
|
||||
onFilterClicked = {},
|
||||
hasActiveFilters = true,
|
||||
usePanoramaCover = true,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,8 +101,8 @@ class HistoryScreenModelStateProvider : PreviewParameterProvider<HistoryScreenMo
|
|||
// KMK -->
|
||||
read = Random.nextBoolean(),
|
||||
lastPageRead = Random.nextLong(1, 10),
|
||||
totalChapters = Random.nextLong(1, 100),
|
||||
readCount = 1,
|
||||
totalCountCalculated = Random.nextLong(1, 100),
|
||||
readCountCalculated = 1,
|
||||
// KMK <--
|
||||
readAt = Date.from(Instant.now()),
|
||||
readDuration = Random.nextLong(),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
package eu.kanade.presentation.history.components
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import eu.kanade.presentation.components.TabbedDialog
|
||||
import eu.kanade.presentation.components.TabbedDialogPaddings
|
||||
import eu.kanade.tachiyomi.ui.history.HistorySettingsScreenModel
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import tachiyomi.domain.history.service.HistoryPreferences
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.i18n.kmk.KMR
|
||||
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.util.collectAsState
|
||||
|
||||
@Composable
|
||||
fun HistoryFilterDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
screenModel: HistorySettingsScreenModel,
|
||||
) {
|
||||
TabbedDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
tabTitles = persistentListOf(
|
||||
stringResource(MR.strings.action_filter),
|
||||
),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(vertical = TabbedDialogPaddings.Vertical)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
FilterSheet(screenModel = screenModel)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.FilterSheet(
|
||||
screenModel: HistorySettingsScreenModel,
|
||||
) {
|
||||
val filterUnfinishedManga by screenModel.historyPreferences.filterUnfinishedManga().collectAsState()
|
||||
TriStateItem(
|
||||
label = stringResource(KMR.strings.action_filter_unfinished_manga),
|
||||
state = filterUnfinishedManga,
|
||||
onClick = { screenModel.toggleFilter(HistoryPreferences::filterUnfinishedManga) },
|
||||
)
|
||||
|
||||
val filterUnfinishedChapter by screenModel.historyPreferences.filterUnfinishedChapter().collectAsState()
|
||||
TriStateItem(
|
||||
label = stringResource(KMR.strings.action_filter_unfinished_chapter),
|
||||
state = filterUnfinishedChapter,
|
||||
onClick = { screenModel.toggleFilter(HistoryPreferences::filterUnfinishedChapter) },
|
||||
)
|
||||
|
||||
val filterNonLibraryManga by screenModel.historyPreferences.filterNonLibraryManga().collectAsState()
|
||||
TriStateItem(
|
||||
label = stringResource(KMR.strings.action_filter_non_library_entries),
|
||||
state = filterNonLibraryManga,
|
||||
onClick = { screenModel.toggleFilter(HistoryPreferences::filterNonLibraryManga) },
|
||||
)
|
||||
|
||||
HorizontalDivider(modifier = Modifier.padding(MaterialTheme.padding.small))
|
||||
|
||||
val panoramaCover by screenModel.historyPreferences.usePanoramaCover().collectAsState()
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.clickable { screenModel.toggleSwitch(HistoryPreferences::usePanoramaCover) }
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = SettingsItemsPaddings.Horizontal),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(KMR.strings.action_panorama_cover),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
)
|
||||
|
||||
Switch(
|
||||
checked = panoramaCover,
|
||||
onCheckedChange = { screenModel.toggleSwitch(HistoryPreferences::usePanoramaCover) },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -38,6 +38,7 @@ import eu.kanade.tachiyomi.util.lang.toTimestampString
|
|||
import exh.debug.DebugToggles
|
||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.i18n.kmk.KMR
|
||||
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
|
||||
import tachiyomi.presentation.core.components.material.padding
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
|
|
@ -139,7 +140,7 @@ fun HistoryItem(
|
|||
if (hasUnread) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Circle,
|
||||
contentDescription = stringResource(MR.strings.unread),
|
||||
contentDescription = stringResource(KMR.strings.action_filter_unfinished_manga),
|
||||
modifier = Modifier
|
||||
.height(8.dp)
|
||||
.padding(end = 4.dp),
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ internal class HistoryWithRelationsProvider : PreviewParameterProvider<HistoryWi
|
|||
// KMK -->
|
||||
read = true,
|
||||
lastPageRead = 5,
|
||||
totalChapters = 5L,
|
||||
readCount = 3L,
|
||||
totalCountCalculated = 5L,
|
||||
readCountCalculated = 3L,
|
||||
// KMK <--
|
||||
readAt = Date(1697247357L),
|
||||
readDuration = 123L,
|
||||
|
|
@ -42,8 +42,8 @@ internal class HistoryWithRelationsProvider : PreviewParameterProvider<HistoryWi
|
|||
// KMK -->
|
||||
read = false,
|
||||
lastPageRead = 5,
|
||||
totalChapters = 5L,
|
||||
readCount = 3L,
|
||||
totalCountCalculated = 5L,
|
||||
readCountCalculated = 3L,
|
||||
// KMK <--
|
||||
readAt = null,
|
||||
readDuration = 123L,
|
||||
|
|
@ -67,8 +67,8 @@ internal class HistoryWithRelationsProvider : PreviewParameterProvider<HistoryWi
|
|||
// KMK -->
|
||||
read = true,
|
||||
lastPageRead = 5,
|
||||
totalChapters = 5L,
|
||||
readCount = 3L,
|
||||
totalCountCalculated = 5L,
|
||||
readCountCalculated = 3L,
|
||||
// KMK <--
|
||||
readAt = Date(1697247357L),
|
||||
readDuration = 123L,
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import tachiyomi.core.common.preference.PreferenceStore
|
|||
import tachiyomi.core.common.storage.AndroidStorageFolderProvider
|
||||
import tachiyomi.domain.backup.service.BackupPreferences
|
||||
import tachiyomi.domain.download.service.DownloadPreferences
|
||||
import tachiyomi.domain.history.service.HistoryPreferences
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.storage.service.StoragePreferences
|
||||
import tachiyomi.domain.updates.service.UpdatesPreferences
|
||||
|
|
@ -52,6 +53,11 @@ class PreferenceModule(val app: Application) : InjektModule {
|
|||
addSingletonFactory {
|
||||
UpdatesPreferences(get())
|
||||
}
|
||||
// KMK -->
|
||||
addSingletonFactory {
|
||||
HistoryPreferences(get())
|
||||
}
|
||||
// KMK <--
|
||||
addSingletonFactory {
|
||||
ReaderPreferences(get())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,15 +15,19 @@ import kotlinx.coroutines.Dispatchers
|
|||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.preference.CheckboxState
|
||||
import tachiyomi.core.common.preference.TriState
|
||||
import tachiyomi.core.common.preference.mapAsCheckboxState
|
||||
import tachiyomi.core.common.util.lang.launchIO
|
||||
import tachiyomi.core.common.util.lang.withIOContext
|
||||
|
|
@ -36,6 +40,7 @@ import tachiyomi.domain.history.interactor.GetHistory
|
|||
import tachiyomi.domain.history.interactor.GetNextChapters
|
||||
import tachiyomi.domain.history.interactor.RemoveHistory
|
||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||
import tachiyomi.domain.history.service.HistoryPreferences
|
||||
import tachiyomi.domain.library.service.LibraryPreferences
|
||||
import tachiyomi.domain.manga.interactor.GetDuplicateLibraryManga
|
||||
import tachiyomi.domain.manga.interactor.GetManga
|
||||
|
|
@ -58,6 +63,9 @@ class HistoryScreenModel(
|
|||
private val updateManga: UpdateManga = Injekt.get(),
|
||||
val snackbarHostState: SnackbarHostState = SnackbarHostState(),
|
||||
private val sourceManager: SourceManager = Injekt.get(),
|
||||
// KMK -->
|
||||
private val historyPreferences: HistoryPreferences = Injekt.get(),
|
||||
// KMK <--
|
||||
) : StateScreenModel<HistoryScreenModel.State>(State()) {
|
||||
|
||||
private val _events: Channel<Event> = Channel(Channel.UNLIMITED)
|
||||
|
|
@ -65,10 +73,25 @@ class HistoryScreenModel(
|
|||
|
||||
init {
|
||||
screenModelScope.launch {
|
||||
state.map { it.searchQuery }
|
||||
.distinctUntilChanged()
|
||||
.flatMapLatest { query ->
|
||||
getHistory.subscribe(query ?: "")
|
||||
// KMK -->
|
||||
combine(
|
||||
// KMK <--
|
||||
state.map { it.searchQuery }
|
||||
.distinctUntilChanged(),
|
||||
// KMK -->
|
||||
getHistoryItemPreferenceFlow()
|
||||
.distinctUntilChanged(),
|
||||
) { query, itemPreferences -> query to itemPreferences }
|
||||
.flatMapLatest { (query, pref) ->
|
||||
// KMK <--
|
||||
getHistory.subscribe(
|
||||
query ?: "",
|
||||
// KMK -->
|
||||
unfinishedManga = pref.filterUnfinishedManga.toBooleanOrNull(),
|
||||
unfinishedChapter = pref.filterUnfinishedChapter.toBooleanOrNull(),
|
||||
nonLibraryEntries = pref.filterNonLibraryManga.toBooleanOrNull(),
|
||||
// KMK <--
|
||||
)
|
||||
.distinctUntilChanged()
|
||||
.catch { error ->
|
||||
logcat(LogPriority.ERROR, error)
|
||||
|
|
@ -79,6 +102,25 @@ class HistoryScreenModel(
|
|||
}
|
||||
.collect { newList -> mutableState.update { it.copy(list = newList) } }
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
getHistoryItemPreferenceFlow()
|
||||
.map { prefs ->
|
||||
listOf(
|
||||
prefs.filterUnfinishedManga,
|
||||
prefs.filterUnfinishedChapter,
|
||||
prefs.filterNonLibraryManga,
|
||||
)
|
||||
.any { it != TriState.DISABLED }
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
mutableState.update { state ->
|
||||
state.copy(hasActiveFilters = it)
|
||||
}
|
||||
}
|
||||
.launchIn(screenModelScope)
|
||||
// KMK <--
|
||||
}
|
||||
|
||||
private fun List<HistoryWithRelations>.toHistoryUiModels(): List<HistoryUiModel> {
|
||||
|
|
@ -237,11 +279,41 @@ class HistoryScreenModel(
|
|||
}
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
private fun getHistoryItemPreferenceFlow(): Flow<ItemPreferences> {
|
||||
return combine(
|
||||
historyPreferences.filterUnfinishedManga().changes(),
|
||||
historyPreferences.filterUnfinishedChapter().changes(),
|
||||
historyPreferences.filterNonLibraryManga().changes(),
|
||||
) { unfinishedManga, unfinishedChapter, nonLibraryManga ->
|
||||
ItemPreferences(
|
||||
filterUnfinishedManga = unfinishedManga,
|
||||
filterUnfinishedChapter = unfinishedChapter,
|
||||
filterNonLibraryManga = nonLibraryManga,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun showFilterDialog() {
|
||||
mutableState.update { it.copy(dialog = Dialog.FilterSheet) }
|
||||
}
|
||||
|
||||
@Immutable
|
||||
private data class ItemPreferences(
|
||||
val filterUnfinishedManga: TriState,
|
||||
val filterUnfinishedChapter: TriState,
|
||||
val filterNonLibraryManga: TriState,
|
||||
)
|
||||
// KMK <--
|
||||
|
||||
@Immutable
|
||||
data class State(
|
||||
val searchQuery: String? = null,
|
||||
val list: List<HistoryUiModel>? = null,
|
||||
val dialog: Dialog? = null,
|
||||
// KMk -->
|
||||
val hasActiveFilters: Boolean = false,
|
||||
// KMK <--
|
||||
)
|
||||
|
||||
sealed interface Dialog {
|
||||
|
|
@ -253,8 +325,21 @@ class HistoryScreenModel(
|
|||
val initialSelection: ImmutableList<CheckboxState<Category>>,
|
||||
) : Dialog
|
||||
data class Migrate(val target: Manga, val current: Manga) : Dialog
|
||||
// KMK -->
|
||||
data object FilterSheet : Dialog
|
||||
// KMK <--
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
private fun TriState.toBooleanOrNull(): Boolean? {
|
||||
return when (this) {
|
||||
TriState.DISABLED -> null
|
||||
TriState.ENABLED_IS -> true
|
||||
TriState.ENABLED_NOT -> false
|
||||
}
|
||||
}
|
||||
// KMK <--
|
||||
|
||||
sealed interface Event {
|
||||
data class OpenChapter(val chapter: Chapter?) : Event
|
||||
data object InternalError : Event
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
package eu.kanade.tachiyomi.ui.history
|
||||
|
||||
import cafe.adriel.voyager.core.model.ScreenModel
|
||||
import tachiyomi.core.common.preference.Preference
|
||||
import tachiyomi.core.common.preference.TriState
|
||||
import tachiyomi.core.common.preference.getAndSet
|
||||
import tachiyomi.domain.history.service.HistoryPreferences
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class HistorySettingsScreenModel(
|
||||
val historyPreferences: HistoryPreferences = Injekt.get(),
|
||||
) : ScreenModel {
|
||||
|
||||
fun toggleFilter(preference: (HistoryPreferences) -> Preference<TriState>) {
|
||||
preference(historyPreferences).getAndSet {
|
||||
it.next()
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleSwitch(preference: (HistoryPreferences) -> Preference<Boolean>) {
|
||||
preference(historyPreferences).getAndSet { !it }
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@ import eu.kanade.presentation.category.components.ChangeCategoryDialog
|
|||
import eu.kanade.presentation.history.HistoryScreen
|
||||
import eu.kanade.presentation.history.components.HistoryDeleteAllDialog
|
||||
import eu.kanade.presentation.history.components.HistoryDeleteDialog
|
||||
import eu.kanade.presentation.history.components.HistoryFilterDialog
|
||||
import eu.kanade.presentation.manga.DuplicateMangaDialog
|
||||
import eu.kanade.presentation.util.Tab
|
||||
import eu.kanade.tachiyomi.R
|
||||
|
|
@ -33,7 +34,6 @@ import eu.kanade.tachiyomi.ui.category.CategoryScreen
|
|||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaScreen
|
||||
import eu.kanade.tachiyomi.ui.reader.ReaderActivity
|
||||
import eu.kanade.tachiyomi.ui.updates.UpdatesSettingsScreenModel
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
|
|
@ -43,6 +43,7 @@ import tachiyomi.core.common.util.lang.launchIO
|
|||
import tachiyomi.domain.chapter.model.Chapter
|
||||
import tachiyomi.i18n.MR
|
||||
import tachiyomi.presentation.core.i18n.stringResource
|
||||
import tachiyomi.presentation.core.util.collectAsState
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
|
|
@ -87,7 +88,8 @@ data object HistoryTab : Tab {
|
|||
val screenModel = rememberScreenModel { HistoryScreenModel() }
|
||||
val state by screenModel.state.collectAsState()
|
||||
// KMK -->
|
||||
val settingsScreenModel = rememberScreenModel { UpdatesSettingsScreenModel() }
|
||||
val settingsScreenModel = rememberScreenModel { HistorySettingsScreenModel() }
|
||||
val usePanoramaCover by settingsScreenModel.historyPreferences.usePanoramaCover().collectAsState()
|
||||
// KMK <--
|
||||
|
||||
HistoryScreen(
|
||||
|
|
@ -99,7 +101,9 @@ data object HistoryTab : Tab {
|
|||
onDialogChange = screenModel::setDialog,
|
||||
onClickFavorite = screenModel::addFavorite,
|
||||
// KMK -->
|
||||
settingsScreenModel = settingsScreenModel,
|
||||
onFilterClicked = screenModel::showFilterDialog,
|
||||
hasActiveFilters = state.hasActiveFilters,
|
||||
usePanoramaCover = usePanoramaCover,
|
||||
// KMK <--
|
||||
)
|
||||
|
||||
|
|
@ -154,6 +158,14 @@ data object HistoryTab : Tab {
|
|||
onDismissRequest = onDismissRequest,
|
||||
)
|
||||
}
|
||||
// KMK -->
|
||||
is HistoryScreenModel.Dialog.FilterSheet -> {
|
||||
HistoryFilterDialog(
|
||||
onDismissRequest = onDismissRequest,
|
||||
screenModel = settingsScreenModel,
|
||||
)
|
||||
}
|
||||
// KMK <--
|
||||
null -> {}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ object HistoryMapper {
|
|||
// KMK -->
|
||||
read = read,
|
||||
lastPageRead = lastPageRead,
|
||||
totalChapters = totalCount.toLong(),
|
||||
readCount = readCount.toLong(),
|
||||
totalCountCalculated = totalCount.toLong(),
|
||||
readCountCalculated = readCount.toLong(),
|
||||
// KMK <--
|
||||
readAt = readAt,
|
||||
readDuration = readDuration,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package tachiyomi.data.history
|
|||
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import logcat.LogPriority
|
||||
import tachiyomi.core.common.util.lang.toLong
|
||||
import tachiyomi.core.common.util.system.logcat
|
||||
import tachiyomi.data.DatabaseHandler
|
||||
import tachiyomi.domain.history.model.History
|
||||
|
|
@ -14,11 +15,23 @@ class HistoryRepositoryImpl(
|
|||
private val handler: DatabaseHandler,
|
||||
) : HistoryRepository {
|
||||
|
||||
override fun getHistory(query: String): Flow<List<HistoryWithRelations>> {
|
||||
override fun getHistory(
|
||||
query: String,
|
||||
// KMK -->
|
||||
unfinishedManga: Boolean?,
|
||||
unfinishedChapter: Boolean?,
|
||||
nonLibraryEntries: Boolean?,
|
||||
// KMK <--
|
||||
): Flow<List<HistoryWithRelations>> {
|
||||
return handler.subscribeToList {
|
||||
historyViewQueries.history(
|
||||
// KMK -->
|
||||
Manga.CHAPTER_SHOW_NOT_BOOKMARKED,
|
||||
Manga.CHAPTER_SHOW_BOOKMARKED,
|
||||
unfinishedManga?.toLong(),
|
||||
unfinishedChapter,
|
||||
nonLibraryEntries,
|
||||
// KMK <--
|
||||
query,
|
||||
HistoryMapper::mapHistoryWithRelations,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -71,17 +71,26 @@ CASE
|
|||
WHEN (chapterFlags & :bookmarkUnmask) != 0 THEN totalCount - bookmarkCount
|
||||
WHEN (chapterFlags & :bookmarkMask) != 0 THEN bookmarkCount
|
||||
ELSE totalCount
|
||||
END AS totalCount,
|
||||
END AS totalCountCalculated,
|
||||
CASE
|
||||
WHEN (chapterFlags & :bookmarkUnmask) != 0 THEN readCount - bookmarkReadCount
|
||||
WHEN (chapterFlags & :bookmarkMask) != 0 THEN bookmarkReadCount
|
||||
ELSE readCount
|
||||
END AS readCount,
|
||||
END AS readCountCalculated,
|
||||
-- KMK <--
|
||||
readAt,
|
||||
readDuration
|
||||
FROM historyView
|
||||
WHERE historyView.readAt > 0
|
||||
-- KMK -->
|
||||
AND (
|
||||
:unfinishedManga IS NULL
|
||||
OR (:unfinishedManga = 1 AND totalCountCalculated > readCountCalculated)
|
||||
OR (:unfinishedManga = 0 AND totalCountCalculated <= readCountCalculated)
|
||||
)
|
||||
AND (:unfinishedChapter IS NULL OR read != :unfinishedChapter)
|
||||
AND (:nonLibraryEntries IS NULL OR favorite != :nonLibraryEntries)
|
||||
-- KMK <--
|
||||
AND maxReadAtChapterId = historyView.chapterId
|
||||
AND lower(historyView.title) LIKE ('%' || :query || '%')
|
||||
ORDER BY readAt DESC;
|
||||
|
|
|
|||
|
|
@ -13,7 +13,21 @@ class GetHistory(
|
|||
return repository.getHistoryByMangaId(mangaId)
|
||||
}
|
||||
|
||||
fun subscribe(query: String): Flow<List<HistoryWithRelations>> {
|
||||
return repository.getHistory(query)
|
||||
fun subscribe(
|
||||
query: String,
|
||||
// KMK -->
|
||||
unfinishedManga: Boolean?,
|
||||
unfinishedChapter: Boolean?,
|
||||
nonLibraryEntries: Boolean?,
|
||||
// KMK <--
|
||||
): Flow<List<HistoryWithRelations>> {
|
||||
return repository.getHistory(
|
||||
query,
|
||||
// KMK -->
|
||||
unfinishedManga,
|
||||
unfinishedChapter,
|
||||
nonLibraryEntries,
|
||||
// KMK <--
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,8 +16,8 @@ data class HistoryWithRelations(
|
|||
// KMK -->
|
||||
val read: Boolean,
|
||||
val lastPageRead: Long,
|
||||
val totalChapters: Long,
|
||||
val readCount: Long,
|
||||
val totalCountCalculated: Long,
|
||||
val readCountCalculated: Long,
|
||||
// KMK <--
|
||||
val readAt: Date?,
|
||||
val readDuration: Long,
|
||||
|
|
@ -33,6 +33,6 @@ data class HistoryWithRelations(
|
|||
|
||||
// KMK -->
|
||||
val unreadCount
|
||||
get() = totalChapters - readCount
|
||||
get() = totalCountCalculated - readCountCalculated
|
||||
// KMK <--
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,14 @@ import tachiyomi.domain.history.model.HistoryWithRelations
|
|||
|
||||
interface HistoryRepository {
|
||||
|
||||
fun getHistory(query: String): Flow<List<HistoryWithRelations>>
|
||||
fun getHistory(
|
||||
query: String,
|
||||
// KMK -->
|
||||
unfinishedManga: Boolean?,
|
||||
unfinishedChapter: Boolean?,
|
||||
nonLibraryEntries: Boolean?,
|
||||
// KMK <--
|
||||
): Flow<List<HistoryWithRelations>>
|
||||
|
||||
suspend fun getLastHistory(): HistoryWithRelations?
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package tachiyomi.domain.history.service
|
||||
|
||||
import tachiyomi.core.common.preference.PreferenceStore
|
||||
import tachiyomi.core.common.preference.TriState
|
||||
import tachiyomi.core.common.preference.getEnum
|
||||
import tachiyomi.domain.updates.service.USE_PANORAMA_COVER_PREF
|
||||
|
||||
class HistoryPreferences(
|
||||
private val preferenceStore: PreferenceStore,
|
||||
) {
|
||||
|
||||
fun filterUnfinishedManga() = preferenceStore.getEnum(
|
||||
"pref_filter_history_unfinished_manga",
|
||||
TriState.DISABLED,
|
||||
)
|
||||
|
||||
fun filterUnfinishedChapter() = preferenceStore.getEnum(
|
||||
"pref_filter_history_unfinished_chapter",
|
||||
TriState.DISABLED,
|
||||
)
|
||||
|
||||
fun filterNonLibraryManga() = preferenceStore.getEnum(
|
||||
"pref_filter_history_non_library_manga",
|
||||
TriState.DISABLED,
|
||||
)
|
||||
|
||||
fun usePanoramaCover() = preferenceStore.getBoolean(
|
||||
USE_PANORAMA_COVER_PREF,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
|
@ -35,8 +35,12 @@ class UpdatesPreferences(
|
|||
|
||||
// KMK -->
|
||||
fun usePanoramaCover() = preferenceStore.getBoolean(
|
||||
"pref_updates_screen_use_panorama_cover",
|
||||
USE_PANORAMA_COVER_PREF,
|
||||
false,
|
||||
)
|
||||
// KMK <--
|
||||
}
|
||||
|
||||
// KMK -->
|
||||
const val USE_PANORAMA_COVER_PREF = "pref_updates_history_screen_use_panorama_cover"
|
||||
// KMK <--
|
||||
|
|
|
|||
|
|
@ -17,6 +17,10 @@
|
|||
<string name="action_open_folder">Open folder</string>
|
||||
<string name="action_clear_manga">Clear manga</string>
|
||||
<string name="action_update">Update</string>
|
||||
<!-- History section -->
|
||||
<string name="action_filter_unfinished_manga">Unfinished manga</string>
|
||||
<string name="action_filter_unfinished_chapter">Unfinished chapter</string>
|
||||
<string name="action_filter_non_library_entries">Non-library entries</string>
|
||||
<!-- Reader -->
|
||||
<!-- Reader Actions -->
|
||||
<string name="action_copy_to_clipboard_first_page">Copy first page to clipboard</string>
|
||||
|
|
|
|||
Loading…
Reference in a new issue