feat(history): Add Filters to History screen (#1433)

* Add Filters to History screen

* Revert favorite condition
This commit is contained in:
Cuong-Tran 2026-01-28 13:07:43 +07:00
parent 3e7dc5c41a
commit f373818d93
No known key found for this signature in database
GPG key ID: 94EFFE320FE7A47C
18 changed files with 368 additions and 68 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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