From c8909961c04e71255252010a9b8ee678b513e8d5 Mon Sep 17 00:00:00 2001 From: AntsyLich <59261191+AntsyLich@users.noreply.github.com> Date: Sun, 5 Nov 2023 21:34:35 +0600 Subject: [PATCH] Implement scanlator filter (#8803) * Implement scanlator filter * Visual improvement to scanlator filter dialog * Review changes + Bug fixes Backup not containing filtered chapters and similar issue fix * Review Changes + Fix SQL query * Lint mamma mia (cherry picked from commit b97aa235480e35b5514b7b1489b9d4413cea66d9) # Conflicts: # app/build.gradle.kts # app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt # app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreator.kt # app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt # app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt # data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt # data/src/main/sqldelight/tachiyomi/migrations/23.sqm # data/src/main/sqldelight/tachiyomi/migrations/26.sqm # domain/src/main/java/tachiyomi/domain/history/interactor/GetNextChapters.kt --- app/build.gradle.kts | 2 +- .../java/eu/kanade/domain/DomainModule.kt | 8 +- .../java/eu/kanade/domain/SYDomainModule.kt | 2 - .../interactor/GetAvailableScanlators.kt | 24 ++++ .../interactor/SyncChaptersWithSource.kt | 8 +- .../domain/chapter/model/ChapterFilter.kt | 19 +-- .../manga/interactor/GetExcludedScanlators.kt | 24 ++++ .../manga/interactor/SetExcludedScanlators.kt | 22 +++ .../eu/kanade/domain/manga/model/Manga.kt | 5 +- .../manga/ChapterSettingsDialog.kt | 45 +++--- .../kanade/presentation/manga/MangaScreen.kt | 5 +- .../manga/components/ScanlatorFilterDialog.kt | 134 ++++++++++++++++++ .../java/eu/kanade/tachiyomi/AppModule.kt | 4 - .../tachiyomi/data/backup/BackupCreator.kt | 12 +- .../tachiyomi/data/backup/BackupRestorer.kt | 8 -- .../data/backup/models/BackupManga.kt | 3 - .../ui/library/LibraryScreenModel.kt | 4 +- .../kanade/tachiyomi/ui/manga/MangaScreen.kt | 32 +++-- .../tachiyomi/ui/manga/MangaScreenModel.kt | 78 +++++----- .../tachiyomi/ui/reader/ReaderViewModel.kt | 12 +- app/src/main/java/exh/debug/DebugFunctions.kt | 4 - .../java/tachiyomi/data/DatabaseAdapter.kt | 13 -- .../main/java/tachiyomi/data/LibraryQuery.kt | 10 +- .../data/chapter/ChapterRepositoryImpl.kt | 34 +++-- .../java/tachiyomi/data/manga/MangaMapper.kt | 16 ++- .../data/manga/MangaRepositoryImpl.kt | 7 - .../sqldelight/tachiyomi/data/chapters.sq | 32 ++++- data/src/main/sqldelight/tachiyomi/data/eh.sq | 4 - .../tachiyomi/data/excluded_scanlators.sq | 22 +++ .../main/sqldelight/tachiyomi/data/mangas.sq | 7 +- .../sqldelight/tachiyomi/migrations/29.sqm | 44 ++++++ .../sqldelight/tachiyomi/view/libraryView.sq | 4 + .../interactor/GetChaptersByMangaId.kt | 4 +- .../interactor/GetMergedChaptersByMangaId.kt | 25 +++- .../chapter/repository/ChapterRepository.kt | 12 +- .../history/interactor/GetNextChapters.kt | 6 +- .../domain/manga/interactor/FetchInterval.kt | 2 +- .../manga/interactor/GetMangaWithChapters.kt | 8 +- .../interactor/SetMangaFilteredScanlators.kt | 17 --- .../tachiyomi/domain/manga/model/Manga.kt | 6 - .../domain/manga/model/MangaUpdate.kt | 3 - i18n/src/main/res/values-fr/strings_sy.xml | 1 - i18n/src/main/res/values-in/strings_sy.xml | 1 - .../src/main/res/values-pt-rBR/strings_sy.xml | 1 - i18n/src/main/res/values-ru/strings_sy.xml | 1 - .../src/main/res/values-zh-rCN/strings_sy.xml | 1 - i18n/src/main/res/values/strings.xml | 3 + i18n/src/main/res/values/strings_sy.xml | 1 - 48 files changed, 503 insertions(+), 237 deletions(-) create mode 100644 app/src/main/java/eu/kanade/domain/chapter/interactor/GetAvailableScanlators.kt create mode 100644 app/src/main/java/eu/kanade/domain/manga/interactor/GetExcludedScanlators.kt create mode 100644 app/src/main/java/eu/kanade/domain/manga/interactor/SetExcludedScanlators.kt create mode 100644 app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt create mode 100644 data/src/main/sqldelight/tachiyomi/data/excluded_scanlators.sq create mode 100644 data/src/main/sqldelight/tachiyomi/migrations/29.sqm delete mode 100644 domain/src/main/java/tachiyomi/domain/manga/interactor/SetMangaFilteredScanlators.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9f664e708..a08958904 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -26,7 +26,7 @@ android { defaultConfig { applicationId = "eu.kanade.tachiyomi.sy" - versionCode = 58 + versionCode = 59 versionName = "1.9.4" buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index fce305a74..de226f046 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -1,11 +1,14 @@ package eu.kanade.domain +import eu.kanade.domain.chapter.interactor.GetAvailableScanlators import eu.kanade.domain.chapter.interactor.SetReadStatus import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource import eu.kanade.domain.download.interactor.DeleteDownload import eu.kanade.domain.extension.interactor.GetExtensionLanguages import eu.kanade.domain.extension.interactor.GetExtensionSources import eu.kanade.domain.extension.interactor.GetExtensionsByType +import eu.kanade.domain.manga.interactor.GetExcludedScanlators +import eu.kanade.domain.manga.interactor.SetExcludedScanlators import eu.kanade.domain.manga.interactor.SetMangaViewerFlags import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.source.interactor.GetEnabledSources @@ -112,6 +115,8 @@ class DomainModule : InjektModule { addFactory { NetworkToLocalManga(get()) } addFactory { UpdateManga(get(), get()) } addFactory { SetMangaCategories(get()) } + addFactory { GetExcludedScanlators(get()) } + addFactory { SetExcludedScanlators(get()) } addSingletonFactory { ReleaseServiceImpl(get(), get()) } addFactory { GetApplicationRelease(get(), get()) } @@ -133,7 +138,8 @@ class DomainModule : InjektModule { addFactory { UpdateChapter(get()) } addFactory { SetReadStatus(get(), get(), get(), get(), get()) } addFactory { ShouldUpdateDbChapter() } - addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get()) } + addFactory { SyncChaptersWithSource(get(), get(), get(), get(), get(), get(), get(), get()) } + addFactory { GetAvailableScanlators(get()) } addSingletonFactory { HistoryRepositoryImpl(get()) } addFactory { GetHistory(get()) } diff --git a/app/src/main/java/eu/kanade/domain/SYDomainModule.kt b/app/src/main/java/eu/kanade/domain/SYDomainModule.kt index 2a5f6d182..c8ed89a5c 100644 --- a/app/src/main/java/eu/kanade/domain/SYDomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/SYDomainModule.kt @@ -52,7 +52,6 @@ import tachiyomi.domain.manga.interactor.InsertFavoriteEntryAlternative import tachiyomi.domain.manga.interactor.InsertFlatMetadata import tachiyomi.domain.manga.interactor.InsertMergedReference import tachiyomi.domain.manga.interactor.SetCustomMangaInfo -import tachiyomi.domain.manga.interactor.SetMangaFilteredScanlators import tachiyomi.domain.manga.interactor.UpdateMergedSettings import tachiyomi.domain.manga.repository.CustomMangaRepository import tachiyomi.domain.manga.repository.FavoritesEntryRepository @@ -88,7 +87,6 @@ class SYDomainModule : InjektModule { addFactory { GetShowLatest(get()) } addFactory { ToggleExcludeFromDataSaver(get()) } addFactory { SetSourceCategories(get()) } - addFactory { SetMangaFilteredScanlators(get()) } addFactory { GetAllManga(get()) } addFactory { GetMangaBySource(get()) } addFactory { DeleteChapters(get()) } diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/GetAvailableScanlators.kt b/app/src/main/java/eu/kanade/domain/chapter/interactor/GetAvailableScanlators.kt new file mode 100644 index 000000000..13bd35e1f --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/chapter/interactor/GetAvailableScanlators.kt @@ -0,0 +1,24 @@ +package eu.kanade.domain.chapter.interactor + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import tachiyomi.domain.chapter.repository.ChapterRepository + +class GetAvailableScanlators( + private val repository: ChapterRepository, +) { + + private fun List.cleanupAvailableScanlators(): Set { + return mapNotNull { it.ifBlank { null } }.toSet() + } + + suspend fun await(mangaId: Long): Set { + return repository.getScanlatorsByMangaId(mangaId) + .cleanupAvailableScanlators() + } + + fun subscribe(mangaId: Long): Flow> { + return repository.getScanlatorsByMangaIdAsFlow(mangaId) + .map { it.cleanupAvailableScanlators() } + } +} diff --git a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt index 7008743c0..d0cb8d857 100644 --- a/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt +++ b/app/src/main/java/eu/kanade/domain/chapter/interactor/SyncChaptersWithSource.kt @@ -2,6 +2,7 @@ package eu.kanade.domain.chapter.interactor import eu.kanade.domain.chapter.model.copyFromSChapter import eu.kanade.domain.chapter.model.toSChapter +import eu.kanade.domain.manga.interactor.GetExcludedScanlators import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.model.toSManga import eu.kanade.tachiyomi.data.download.DownloadManager @@ -34,6 +35,7 @@ class SyncChaptersWithSource( private val updateManga: UpdateManga, private val updateChapter: UpdateChapter, private val getChaptersByMangaId: GetChaptersByMangaId, + private val getExcludedScanlators: GetExcludedScanlators, ) { /** @@ -234,6 +236,10 @@ class SyncChaptersWithSource( val reAddedUrls = reAdded.map { it.url }.toHashSet() - return updatedToAdd.filterNot { it.url in reAddedUrls } + val excludedScanlators = getExcludedScanlators.await(manga.id).toHashSet() + + return updatedToAdd.filterNot { + it.url in reAddedUrls || it.scanlator in excludedScanlators + } } } diff --git a/app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt b/app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt index 2833c9ec5..864eb2836 100644 --- a/app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt +++ b/app/src/main/java/eu/kanade/domain/chapter/model/ChapterFilter.kt @@ -3,7 +3,6 @@ package eu.kanade.domain.chapter.model import eu.kanade.domain.manga.model.downloadedFilter import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.ui.manga.ChapterList -import exh.md.utils.MdUtil import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.chapter.service.getChapterSort import tachiyomi.domain.manga.model.Manga @@ -16,7 +15,7 @@ import tachiyomi.source.local.isLocal */ fun List.applyFilters( manga: Manga, - downloadManager: DownloadManager/* SY --> */, + downloadManager: DownloadManager,/* SY --> */ mergedManga: Map, /* SY <-- */ ): List { val isLocalManga = manga.isLocal() @@ -41,14 +40,6 @@ fun List.applyFilters( downloaded || isLocalManga } } - // SY --> - .filter { chapter -> - manga.filteredScanlators.isNullOrEmpty() || - MdUtil.getScanlators(chapter.scanlator).any { group -> - manga.filteredScanlators!!.contains(group) - } - } - // SY <-- .sortedWith(getChapterSort(manga)) } @@ -65,13 +56,5 @@ fun List.applyFilters(manga: Manga): Sequence applyFilter(unreadFilter) { !chapter.read } } .filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } } .filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } } - // SY --> - .filter { chapter -> - manga.filteredScanlators.isNullOrEmpty() || - MdUtil.getScanlators(chapter.chapter.scanlator).any { group -> - manga.filteredScanlators!!.contains(group) - } - } - // SY <-- .sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) } } diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/GetExcludedScanlators.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/GetExcludedScanlators.kt new file mode 100644 index 000000000..dc326f209 --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/GetExcludedScanlators.kt @@ -0,0 +1,24 @@ +package eu.kanade.domain.manga.interactor + +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map +import tachiyomi.data.DatabaseHandler + +class GetExcludedScanlators( + private val handler: DatabaseHandler, +) { + + suspend fun await(mangaId: Long): Set { + return handler.awaitList { + excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(mangaId) + } + .toSet() + } + + fun subscribe(mangaId: Long): Flow> { + return handler.subscribeToList { + excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(mangaId) + } + .map { it.toSet() } + } +} diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/SetExcludedScanlators.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/SetExcludedScanlators.kt new file mode 100644 index 000000000..a52fb9afd --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/SetExcludedScanlators.kt @@ -0,0 +1,22 @@ +package eu.kanade.domain.manga.interactor + +import tachiyomi.data.DatabaseHandler + +class SetExcludedScanlators( + private val handler: DatabaseHandler, +) { + + suspend fun await(mangaId: Long, excludedScanlators: Set) { + handler.await(inTransaction = true) { + val currentExcluded = handler.awaitList { + excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(mangaId) + }.toSet() + val toAdd = excludedScanlators.minus(currentExcluded) + for (scanlator in toAdd) { + excluded_scanlatorsQueries.insert(mangaId, scanlator) + } + val toRemove = currentExcluded.minus(excludedScanlators) + excluded_scanlatorsQueries.remove(mangaId, toRemove) + } + } +} diff --git a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt index 968fa08ea..fe35f61bd 100644 --- a/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/model/Manga.kt @@ -33,10 +33,7 @@ val Manga.downloadedFilter: TriState fun Manga.chaptersFiltered(): Boolean { return unreadFilter != TriState.DISABLED || downloadedFilter != TriState.DISABLED || - bookmarkedFilter != TriState.DISABLED || - // SY --> - !filteredScanlators.isNullOrEmpty() - // SY <-- + bookmarkedFilter != TriState.DISABLED } fun Manga.forceDownloaded(): Boolean { return favorite && Injekt.get().downloadedOnly().get() diff --git a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt index 6940389b2..e29668177 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/ChapterSettingsDialog.kt @@ -14,6 +14,7 @@ import androidx.compose.material.icons.outlined.PeopleAlt import androidx.compose.material3.AlertDialog import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.material3.TextButton @@ -37,6 +38,7 @@ import tachiyomi.presentation.core.components.LabeledCheckbox import tachiyomi.presentation.core.components.RadioItem import tachiyomi.presentation.core.components.SortItem import tachiyomi.presentation.core.components.TriStateItem +import tachiyomi.presentation.core.theme.active @Composable fun ChapterSettingsDialog( @@ -45,13 +47,12 @@ fun ChapterSettingsDialog( onDownloadFilterChanged: (TriState) -> Unit, onUnreadFilterChanged: (TriState) -> Unit, onBookmarkedFilterChanged: (TriState) -> Unit, + scanlatorFilterActive: Boolean, + onScanlatorFilterClicked: (() -> Unit), onSortModeChanged: (Long) -> Unit, onDisplayModeChanged: (Long) -> Unit, onSetAsDefault: (applyToExistingManga: Boolean) -> Unit, onResetToDefault: () -> Unit, - // SY --> - onClickShowScanlatorSelection: (() -> Unit)?, - // SY <-- ) { var showSetAsDefaultDialog by rememberSaveable { mutableStateOf(false) } if (showSetAsDefaultDialog) { @@ -94,14 +95,14 @@ fun ChapterSettingsDialog( 0 -> { FilterPage( downloadFilter = manga?.downloadedFilter ?: TriState.DISABLED, - onDownloadFilterChanged = onDownloadFilterChanged.takeUnless { manga?.forceDownloaded() == true }, + onDownloadFilterChanged = onDownloadFilterChanged + .takeUnless { manga?.forceDownloaded() == true }, unreadFilter = manga?.unreadFilter ?: TriState.DISABLED, onUnreadFilterChanged = onUnreadFilterChanged, bookmarkedFilter = manga?.bookmarkedFilter ?: TriState.DISABLED, onBookmarkedFilterChanged = onBookmarkedFilterChanged, - // SY --> - onClickShowScanlatorSelection = onClickShowScanlatorSelection, - // SY <-- + scanlatorFilterActive = scanlatorFilterActive, + onScanlatorFilterClicked = onScanlatorFilterClicked, ) } 1 -> { @@ -130,9 +131,8 @@ private fun ColumnScope.FilterPage( onUnreadFilterChanged: (TriState) -> Unit, bookmarkedFilter: TriState, onBookmarkedFilterChanged: (TriState) -> Unit, - // SY --> - onClickShowScanlatorSelection: (() -> Unit)?, - // SY <-- + scanlatorFilterActive: Boolean, + onScanlatorFilterClicked: (() -> Unit), ) { TriStateItem( label = stringResource(R.string.label_downloaded), @@ -149,23 +149,20 @@ private fun ColumnScope.FilterPage( state = bookmarkedFilter, onClick = onBookmarkedFilterChanged, ) - // SY --> - if (onClickShowScanlatorSelection != null) { - SetScanlatorsItem(onClickShowScanlatorSelection) - } - // SY <-- + ScanlatorFilterItem( + active = scanlatorFilterActive, + onClick = onScanlatorFilterClicked, + ) } -// SY --> @Composable -private fun SetScanlatorsItem( - onClickShowScanlatorSelection: () -> Unit, +fun ScanlatorFilterItem( + active: Boolean, + onClick: () -> Unit, ) { Row( modifier = Modifier - .clickable( - onClick = onClickShowScanlatorSelection, - ) + .clickable(onClick = onClick) .fillMaxWidth() .padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp), verticalAlignment = Alignment.CenterVertically, @@ -174,6 +171,11 @@ private fun SetScanlatorsItem( Icon( imageVector = Icons.Outlined.PeopleAlt, contentDescription = null, + tint = if (active) { + MaterialTheme.colorScheme.active + } else { + LocalContentColor.current + }, ) Text( text = stringResource(R.string.scanlator), @@ -181,7 +183,6 @@ private fun SetScanlatorsItem( ) } } -// SY <-- @Composable private fun ColumnScope.SortPage( diff --git a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt index 80f9baf7b..624cd8b99 100644 --- a/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt @@ -48,7 +48,6 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.util.fastAll import androidx.compose.ui.util.fastAny import androidx.compose.ui.util.fastMap -import eu.kanade.domain.manga.model.chaptersFiltered import eu.kanade.presentation.manga.components.ChapterDownloadAction import eu.kanade.presentation.manga.components.ChapterHeader import eu.kanade.presentation.manga.components.ExpandableMangaDescription @@ -375,7 +374,7 @@ private fun MangaScreenSmallImpl( title = state.manga.title, titleAlphaProvider = { animatedTitleAlpha }, backgroundAlphaProvider = { animatedBgAlpha }, - hasFilters = state.manga.chaptersFiltered(), + hasFilters = state.filterActive, onBackClicked = internalOnBackPressed, onClickFilter = onFilterClicked, onClickShare = onShareClicked, @@ -705,7 +704,7 @@ fun MangaScreenLargeImpl( title = state.manga.title, titleAlphaProvider = { if (isAnySelected) 1f else 0f }, backgroundAlphaProvider = { 1f }, - hasFilters = state.manga.chaptersFiltered(), + hasFilters = state.filterActive, onBackClicked = internalOnBackPressed, onClickFilter = onFilterButtonClicked, onClickShare = onShareClicked, diff --git a/app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt b/app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt new file mode 100644 index 000000000..07d3510d3 --- /dev/null +++ b/app/src/main/java/eu/kanade/presentation/manga/components/ScanlatorFilterDialog.kt @@ -0,0 +1,134 @@ +package eu.kanade.presentation.manga.components + +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.CheckBoxOutlineBlank +import androidx.compose.material.icons.rounded.DisabledByDefault +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.LocalContentColor +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.material3.minimumInteractiveComponentSize +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.runtime.toMutableStateList +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.DialogProperties +import eu.kanade.tachiyomi.R +import tachiyomi.presentation.core.components.material.TextButton +import tachiyomi.presentation.core.components.material.padding +import tachiyomi.presentation.core.util.isScrolledToEnd +import tachiyomi.presentation.core.util.isScrolledToStart + +@Composable +fun ScanlatorFilterDialog( + availableScanlators: Set, + excludedScanlators: Set, + onDismissRequest: () -> Unit, + onConfirm: (Set) -> Unit, +) { + val sortedAvailableScanlators = remember(availableScanlators) { + availableScanlators.sortedWith(compareBy(String.CASE_INSENSITIVE_ORDER) { it }) + } + val mutableExcludedScanlators = remember(excludedScanlators) { excludedScanlators.toMutableStateList() } + AlertDialog( + onDismissRequest = onDismissRequest, + title = { Text(text = stringResource(R.string.exclude_scanlators)) }, + text = textFunc@{ + if (sortedAvailableScanlators.isEmpty()) { + Text(text = stringResource(R.string.no_scanlators_found)) + return@textFunc + } + Box { + val state = rememberLazyListState() + LazyColumn(state = state) { + sortedAvailableScanlators.forEach { scanlator -> + item { + val isExcluded = mutableExcludedScanlators.contains(scanlator) + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .clickable { + if (isExcluded) { + mutableExcludedScanlators.remove(scanlator) + } else { + mutableExcludedScanlators.add(scanlator) + } + } + .minimumInteractiveComponentSize() + .clip(MaterialTheme.shapes.small) + .fillMaxWidth() + .padding(horizontal = MaterialTheme.padding.small), + ) { + Icon( + imageVector = if (isExcluded) { + Icons.Rounded.DisabledByDefault + } else { + Icons.Rounded.CheckBoxOutlineBlank + }, + tint = if (isExcluded) { + MaterialTheme.colorScheme.primary + } else { + LocalContentColor.current + }, + contentDescription = null, + ) + Text( + text = scanlator, + style = MaterialTheme.typography.bodyMedium, + modifier = Modifier.padding(start = 24.dp), + ) + } + } + } + } + if (!state.isScrolledToStart()) HorizontalDivider(modifier = Modifier.align(Alignment.TopCenter)) + if (!state.isScrolledToEnd()) HorizontalDivider(modifier = Modifier.align(Alignment.BottomCenter)) + } + }, + properties = DialogProperties( + usePlatformDefaultWidth = true, + ), + confirmButton = { + FlowRow { + if (sortedAvailableScanlators.isEmpty()) { + TextButton(onClick = onDismissRequest) { + Text(text = stringResource(R.string.action_cancel)) + } + return@FlowRow + } + TextButton(onClick = mutableExcludedScanlators::clear) { + Text(text = stringResource(R.string.action_reset)) + } + Spacer(modifier = Modifier.weight(1f)) + TextButton(onClick = onDismissRequest) { + Text(text = stringResource(R.string.action_cancel)) + } + TextButton( + onClick = { + onConfirm(mutableExcludedScanlators.toSet()) + onDismissRequest() + }, + ) { + Text(text = stringResource(R.string.action_ok)) + } + } + }, + ) +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt index 249c95019..1f275e3a1 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/AppModule.kt @@ -47,7 +47,6 @@ import tachiyomi.data.DatabaseHandler import tachiyomi.data.DateColumnAdapter import tachiyomi.data.History import tachiyomi.data.Mangas -import tachiyomi.data.StringListAndColumnAdapter import tachiyomi.data.StringListColumnAdapter import tachiyomi.data.UpdateStrategyColumnAdapter import tachiyomi.domain.UnsortedPreferences @@ -126,9 +125,6 @@ class AppModule(val app: Application) : InjektModule { mangasAdapter = Mangas.Adapter( genreAdapter = StringListColumnAdapter, update_strategyAdapter = UpdateStrategyColumnAdapter, - // SY --> - filtered_scanlatorsAdapter = StringListAndColumnAdapter, - // SY <-- ), ) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreator.kt index 29d69b47e..29dfb79d1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupCreator.kt @@ -23,6 +23,7 @@ import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK import eu.kanade.tachiyomi.data.backup.BackupConst.BACKUP_TRACK_MASK import eu.kanade.tachiyomi.data.backup.models.Backup import eu.kanade.tachiyomi.data.backup.models.BackupCategory +import eu.kanade.tachiyomi.data.backup.models.BackupChapter import eu.kanade.tachiyomi.data.backup.models.BackupFlatMetadata import eu.kanade.tachiyomi.data.backup.models.BackupHistory import eu.kanade.tachiyomi.data.backup.models.BackupManga @@ -252,10 +253,15 @@ class BackupCreator( // Check if user wants chapter information in backup if (options and BACKUP_CHAPTER_MASK == BACKUP_CHAPTER) { // Backup all the chapters - val chapters = handler.awaitList { chaptersQueries.getChaptersByMangaId(manga.id, backupChapterMapper) } - if (chapters.isNotEmpty()) { - mangaObject.chapters = chapters + handler.awaitList { + chaptersQueries.getChaptersByMangaId( + mangaId = manga.id, + applyScanlatorFilter = 0, // false + mapper = backupChapterMapper, + ) } + .takeUnless(List::isEmpty) + ?.let { mangaObject.chapters = it } } // Check if user wants category information in backup diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt index 7486bf537..0b4db9b7c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/BackupRestorer.kt @@ -27,7 +27,6 @@ import eu.kanade.tachiyomi.util.system.createFileInCacheDir import exh.EXHMigrations import exh.source.MERGED_SOURCE_ID import exh.util.nullIfBlank -import exh.util.nullIfEmpty import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.isActive import tachiyomi.core.preference.AndroidPreferenceStore @@ -35,7 +34,6 @@ import tachiyomi.core.preference.PreferenceStore import tachiyomi.data.DatabaseHandler import tachiyomi.data.Manga_sync import tachiyomi.data.Mangas -import tachiyomi.data.StringListAndColumnAdapter import tachiyomi.data.UpdateStrategyColumnAdapter import tachiyomi.data.manga.MangaMapper import tachiyomi.data.manga.MergedMangaMapper @@ -365,9 +363,6 @@ class BackupRestorer( coverLastModified = manga.coverLastModified, dateAdded = manga.dateAdded, mangaId = manga.id, - // SY --> - filteredScanlators = manga.filteredScanlators?.let(StringListAndColumnAdapter::encode), - // SY <-- updateStrategy = manga.updateStrategy.let(UpdateStrategyColumnAdapter::encode), ) } @@ -530,9 +525,6 @@ class BackupRestorer( chapterFlags = manga.chapterFlags, coverLastModified = manga.coverLastModified, dateAdded = manga.dateAdded, - // SY --> - filteredScanlators = manga.filteredScanlators?.nullIfEmpty(), - // SY <-- updateStrategy = manga.updateStrategy, ) mangasQueries.selectLastInsertedRowId() diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt index 1f10d2354..3f2980ba7 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/models/BackupManga.kt @@ -4,7 +4,6 @@ import eu.kanade.tachiyomi.source.model.UpdateStrategy import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode import kotlinx.serialization.Serializable import kotlinx.serialization.protobuf.ProtoNumber -import tachiyomi.data.StringListAndColumnAdapter import tachiyomi.domain.chapter.model.Chapter import tachiyomi.domain.manga.model.CustomMangaInfo import tachiyomi.domain.manga.model.Manga @@ -79,7 +78,6 @@ data class BackupManga( chapterFlags = this@BackupManga.chapterFlags.toLong(), updateStrategy = this@BackupManga.updateStrategy, lastModifiedAt = this@BackupManga.lastModifiedAt, - filteredScanlators = this@BackupManga.filtered_scanlators?.let(StringListAndColumnAdapter::decode), ) } @@ -141,7 +139,6 @@ data class BackupManga( lastModifiedAt = manga.lastModifiedAt, favoriteModifiedAt = manga.favoriteModifiedAt, // SY --> - filtered_scanlators = manga.filteredScanlators?.let(StringListAndColumnAdapter::encode), ).also { backupManga -> customMangaInfo?.let { backupManga.customTitle = it.title diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt index 3b2695706..0a951dc8d 100755 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryScreenModel.kt @@ -602,9 +602,9 @@ class LibraryScreenModel( // SY --> val mergedManga = getMergedMangaById.await(manga.id).associateBy { it.id } return if (manga.id == MERGED_SOURCE_ID) { - getMergedChaptersByMangaId.await(manga.id) + getMergedChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) } else { - getChaptersByMangaId.await(manga.id) + getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) }.getNextUnread(manga, downloadManager, mergedManga) // SY <-- } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt index b752c2c99..979ba6d5e 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreen.kt @@ -33,7 +33,7 @@ import eu.kanade.presentation.manga.EditCoverAction import eu.kanade.presentation.manga.MangaScreen import eu.kanade.presentation.manga.components.DeleteChaptersDialog import eu.kanade.presentation.manga.components.MangaCoverDialog -import eu.kanade.presentation.manga.components.SelectScanlatorsDialog +import eu.kanade.presentation.manga.components.ScanlatorFilterDialog import eu.kanade.presentation.manga.components.SetIntervalDialog import eu.kanade.presentation.util.AssistContentScreen import eu.kanade.presentation.util.Screen @@ -156,9 +156,17 @@ class MangaScreen( // SY --> onWebViewClicked = { if (successState.mergedData == null) { - openMangaInWebView(navigator, screenModel.manga, screenModel.source) + openMangaInWebView( + navigator, + screenModel.manga, + screenModel.source + ) } else { - openMergedMangaWebview(context, navigator, successState.mergedData) + openMergedMangaWebview( + context, + navigator, + successState.mergedData + ) } }.takeIf { isHttpSource }, // SY <-- @@ -199,9 +207,7 @@ class MangaScreen( onInvertSelection = screenModel::invertSelection, ) - // SY --> var showScanlatorsDialog by remember { mutableStateOf(false) } - // SY <-- val onDismissRequest = { screenModel.dismissDialog() } when (val dialog = successState.dialog) { @@ -240,9 +246,8 @@ class MangaScreen( onDisplayModeChanged = screenModel::setDisplayMode, onSetAsDefault = screenModel::setCurrentSettingsAsDefault, onResetToDefault = screenModel::resetToDefaultSettings, - // SY --> - onClickShowScanlatorSelection = { showScanlatorsDialog = true }.takeIf { successState.scanlators.size > 1 }, - // SY <-- + scanlatorFilterActive = successState.scanlatorFilterActive, + onScanlatorFilterClicked = { showScanlatorsDialog = true }, ) MangaScreenModel.Dialog.TrackSheet -> { NavigatorAdaptiveSheet( @@ -306,16 +311,15 @@ class MangaScreen( } // SY <-- } - // SY --> + if (showScanlatorsDialog) { - SelectScanlatorsDialog( + ScanlatorFilterDialog( + availableScanlators = successState.availableScanlators, + excludedScanlators = successState.excludedScanlators, onDismissRequest = { showScanlatorsDialog = false }, - availableScanlators = successState.scanlators, - initialSelectedScanlators = successState.manga.filteredScanlators ?: successState.scanlators, - onSelectScanlators = screenModel::setScanlatorFilter, + onConfirm = screenModel::setExcludedScanlators, ) } - // SY <-- } private fun continueReading(context: Context, unreadChapter: Chapter?) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt index 06cb499a2..f1d320460 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt @@ -11,11 +11,15 @@ import cafe.adriel.voyager.core.model.screenModelScope import eu.kanade.core.preference.asState import eu.kanade.core.util.addOrRemove import eu.kanade.core.util.insertSeparators +import eu.kanade.domain.chapter.interactor.GetAvailableScanlators import eu.kanade.domain.chapter.interactor.SetReadStatus import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource +import eu.kanade.domain.manga.interactor.GetExcludedScanlators +import eu.kanade.domain.manga.interactor.SetExcludedScanlators import eu.kanade.domain.manga.interactor.GetPagePreviews import eu.kanade.domain.manga.interactor.UpdateManga import eu.kanade.domain.manga.model.PagePreview +import eu.kanade.domain.manga.model.chaptersFiltered import eu.kanade.domain.manga.model.copyFrom import eu.kanade.domain.manga.model.downloadedFilter import eu.kanade.domain.manga.model.toSManga @@ -106,7 +110,6 @@ import tachiyomi.domain.manga.interactor.InsertMergedReference import tachiyomi.domain.manga.interactor.NetworkToLocalManga import tachiyomi.domain.manga.interactor.SetCustomMangaInfo import tachiyomi.domain.manga.interactor.SetMangaChapterFlags -import tachiyomi.domain.manga.interactor.SetMangaFilteredScanlators import tachiyomi.domain.manga.interactor.UpdateMergedSettings import tachiyomi.domain.manga.model.CustomMangaInfo import tachiyomi.domain.manga.model.Manga @@ -141,7 +144,6 @@ class MangaScreenModel( // SY --> private val sourceManager: SourceManager = Injekt.get(), private val getManga: GetManga = Injekt.get(), - private val setMangaFilteredScanlators: SetMangaFilteredScanlators = Injekt.get(), private val getMergedChaptersByMangaId: GetMergedChaptersByMangaId = Injekt.get(), private val getMergedMangaById: GetMergedMangaById = Injekt.get(), private val getMergedReferencesById: GetMergedReferencesById = Injekt.get(), @@ -157,6 +159,9 @@ class MangaScreenModel( private val setCustomMangaInfo: SetCustomMangaInfo = Injekt.get(), // SY <-- private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(), + private val getAvailableScanlators: GetAvailableScanlators = Injekt.get(), + private val getExcludedScanlators: GetExcludedScanlators = Injekt.get(), + private val setExcludedScanlators: SetExcludedScanlators = Injekt.get(), private val setMangaChapterFlags: SetMangaChapterFlags = Injekt.get(), private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(), private val setReadStatus: SetReadStatus = Injekt.get(), @@ -238,11 +243,11 @@ class MangaScreenModel( init { screenModelScope.launchIO { - getMangaAndChapters.subscribe(mangaId) + getMangaAndChapters.subscribe(mangaId, applyScanlatorFilter = true) .distinctUntilChanged() // SY --> .combine( - getMergedChaptersByMangaId.subscribe(mangaId, true) + getMergedChaptersByMangaId.subscribe(mangaId, true, applyScanlatorFilter = true) .distinctUntilChanged(), ) { (manga, chapters), mergedChapters -> if (manga.source == MERGED_SOURCE_ID) { @@ -311,19 +316,38 @@ class MangaScreenModel( // SY --> meta = raiseMetadata(flatMetadata, it.source), mergedData = mergedData, - scanlators = getChapterScanlators(manga, chapters), // SY <-- ) } } } + screenModelScope.launchIO { + getExcludedScanlators.subscribe(mangaId) + .distinctUntilChanged() + .collectLatest { excludedScanlators -> + updateSuccessState { + it.copy(excludedScanlators = excludedScanlators) + } + } + } + + screenModelScope.launchIO { + getAvailableScanlators.subscribe(mangaId) + .distinctUntilChanged() + .collectLatest { availableScanlators -> + updateSuccessState { + it.copy(availableScanlators = availableScanlators) + } + } + } + observeDownloads() screenModelScope.launchIO { val manga = getMangaAndChapters.awaitManga(mangaId) // SY --> - val chapters = (if (manga.source == MERGED_SOURCE_ID) getMergedChaptersByMangaId.await(mangaId) else getMangaAndChapters.awaitChapters(mangaId)) + val chapters = (if (manga.source == MERGED_SOURCE_ID) getMergedChaptersByMangaId.await(mangaId, applyScanlatorFilter = true) else getMangaAndChapters.awaitChapters(mangaId, applyScanlatorFilter = true)) .toChapterListItems(manga, null) val mergedData = getMergedReferencesById.await(mangaId).takeIf { it.isNotEmpty() }?.let { references -> MergedMangaData( @@ -351,6 +375,8 @@ class MangaScreenModel( source = source, isFromSource = isFromSource, chapters = chapters, + availableScanlators = getAvailableScanlators.await(mangaId), + excludedScanlators = getExcludedScanlators.await(mangaId), isRefreshingData = needRefreshInfo || needRefreshChapter, dialog = null, // SY --> @@ -365,7 +391,6 @@ class MangaScreenModel( } else { PagePreviewState.Unused }, - scanlators = getChapterScanlators(manga, chapters.map { it.chapter }), alwaysShowReadingProgress = readerPreferences.preserveReadingPosition().get() && manga.isEhBasedManga(), // SY <-- ) @@ -424,15 +449,6 @@ class MangaScreenModel( } // SY --> - private fun getChapterScanlators(manga: Manga, chapters: List): List { - return if (manga.isEhBasedManga()) { - emptyList() - } else { - chapters.flatMap { MdUtil.getScanlators(it.scanlator) } - .distinct() - } - } - private fun raiseMetadata(flatMetadata: FlatMetadata?, source: Source): RaisedSearchMetadata? { return if (flatMetadata != null) { val metaClass = source.getMainSource>()?.metaClass @@ -1299,15 +1315,6 @@ class MangaScreenModel( } } - // SY --> - fun setScanlatorFilter(filteredScanlators: List) { - val manga = manga ?: return - screenModelScope.launchIO { - setMangaFilteredScanlators.awaitSetFilteredScanlators(manga, filteredScanlators) - } - } - // SY <-- - /** * Sets the active display mode. * @param mode the mode to set. @@ -1535,6 +1542,12 @@ class MangaScreenModel( updateSuccessState { it.copy(dialog = Dialog.FullCover) } } + fun setExcludedScanlators(excludedScanlators: Set) { + screenModelScope.launchIO { + setExcludedScanlators.await(mangaId, excludedScanlators) + } + } + // SY --> fun showEditMangaInfoDialog() { mutableState.update { state -> @@ -1570,6 +1583,8 @@ class MangaScreenModel( val source: Source, val isFromSource: Boolean, val chapters: List, + val availableScanlators: Set, + val excludedScanlators: Set, val trackItems: List = emptyList(), val isRefreshingData: Boolean = false, val dialog: MangaScreenModel.Dialog? = null, @@ -1581,11 +1596,9 @@ class MangaScreenModel( val showMergeInOverflow: Boolean, val showMergeWithAnother: Boolean, val pagePreviewsState: PagePreviewState, - val scanlators: List, val alwaysShowReadingProgress: Boolean, // SY <-- ) : State { - val processedChapters by lazy { chapters.applyFilters(manga).toList() } @@ -1617,6 +1630,12 @@ class MangaScreenModel( } } + val scanlatorFilterActive: Boolean + get() = excludedScanlators.intersect(availableScanlators).isNotEmpty() + + val filterActive: Boolean + get() = scanlatorFilterActive || manga.chaptersFiltered() + val trackingAvailable: Boolean get() = trackItems.isNotEmpty() @@ -1638,11 +1657,6 @@ class MangaScreenModel( .filter { (chapter) -> applyFilter(unreadFilter) { !chapter.read } } .filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } } .filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } } - // SY --> - .filter { chapter -> - manga.filteredScanlators.isNullOrEmpty() || MdUtil.getScanlators(chapter.chapter.scanlator).any { group -> manga.filteredScanlators!!.contains(group) } - } - // SY <-- .sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 12d1529f8..e94ff80ce 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -181,10 +181,10 @@ class ReaderViewModel @JvmOverloads constructor( // SY --> val (chapters, mangaMap) = runBlocking { if (manga.source == MERGED_SOURCE_ID) { - getMergedChaptersByMangaId.await(manga.id) to getMergedMangaById.await(manga.id) + getMergedChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) to getMergedMangaById.await(manga.id) .associateBy { it.id } } else { - getChaptersByMangaId.await(manga.id) to null + getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) to null } } fun isChapterDownloaded(chapter: Chapter): Boolean { @@ -220,13 +220,7 @@ class ReaderViewModel @JvmOverloads constructor( ) || // SY <-- (manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) || - (manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_NOT_BOOKMARKED && it.bookmark) || - // SY --> - ( - manga.filteredScanlators != null && MdUtil.getScanlators(it.scanlator) - .none { group -> manga.filteredScanlators!!.contains(group) } - ) - // SY <-- + (manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_NOT_BOOKMARKED && it.bookmark) } else -> false } diff --git a/app/src/main/java/exh/debug/DebugFunctions.kt b/app/src/main/java/exh/debug/DebugFunctions.kt index bc0875812..7a00cfd81 100644 --- a/app/src/main/java/exh/debug/DebugFunctions.kt +++ b/app/src/main/java/exh/debug/DebugFunctions.kt @@ -339,9 +339,5 @@ object DebugFunctions { runBlocking { handler.await { ehQueries.migrateAllNhentaiToOtherLang(NHentai.otherId, sources) } } } - fun resetFilteredScanlatorsForAllManga() { - runBlocking { handler.await { ehQueries.resetFilteredScanlatorsForAllManga() } } - } - fun exportProtobufScheme() = ProtoBufSchemaGenerator.generateSchemaText(Backup.serializer().descriptor) } diff --git a/data/src/main/java/tachiyomi/data/DatabaseAdapter.kt b/data/src/main/java/tachiyomi/data/DatabaseAdapter.kt index a6f169abc..596a31286 100644 --- a/data/src/main/java/tachiyomi/data/DatabaseAdapter.kt +++ b/data/src/main/java/tachiyomi/data/DatabaseAdapter.kt @@ -27,16 +27,3 @@ object UpdateStrategyColumnAdapter : ColumnAdapter { override fun encode(value: UpdateStrategy): Long = value.ordinal.toLong() } - -// SY --> -private const val LIST_OF_STRINGS_AND_SEPARATOR = " & " -object StringListAndColumnAdapter : ColumnAdapter, String> { - override fun decode(databaseValue: String) = - if (databaseValue.isEmpty()) { - emptyList() - } else { - databaseValue.split(LIST_OF_STRINGS_AND_SEPARATOR) - } - override fun encode(value: List) = value.joinToString(separator = LIST_OF_STRINGS_AND_SEPARATOR) -} -// SY <-- diff --git a/data/src/main/java/tachiyomi/data/LibraryQuery.kt b/data/src/main/java/tachiyomi/data/LibraryQuery.kt index 0e5047610..b631a2005 100644 --- a/data/src/main/java/tachiyomi/data/LibraryQuery.kt +++ b/data/src/main/java/tachiyomi/data/LibraryQuery.kt @@ -27,7 +27,7 @@ private val mapper = { cursor: SqlCursor -> chapter_flags = cursor.getLong(15)!!, cover_last_modified = cursor.getLong(16)!!, date_added = cursor.getLong(17)!!, - filtered_scanlators = cursor.getString(18)?.let(StringListAndColumnAdapter::decode), + filtered_scanlators = null, update_strategy = UpdateStrategyColumnAdapter.decode(cursor.getLong(19)!!), calculate_interval = cursor.getLong(20)!!, last_modified_at = cursor.getLong(21)!!, @@ -71,8 +71,12 @@ class LibraryQuery( coalesce(max(chapters.date_fetch), 0) AS fetchedAt, sum(chapters.bookmark) AS bookmarkCount FROM chapters + LEFT JOIN excluded_scanlators + ON chapters.manga_id = excluded_scanlators.manga_id + AND chapters.scanlator = excluded_scanlators.scanlator LEFT JOIN history ON chapters._id = history.chapter_id + WHERE excluded_scanlators.scanlator IS NULL GROUP BY chapters.manga_id ) AS C ON M._id = C.manga_id @@ -106,10 +110,14 @@ class LibraryQuery( coalesce(max(chapters.date_fetch), 0) AS fetchedAt, sum(chapters.bookmark) AS bookmarkCount FROM chapters + LEFT JOIN excluded_scanlators + ON chapters.manga_id = excluded_scanlators.manga_id + AND chapters.scanlator = excluded_scanlators.scanlator LEFT JOIN history ON chapters._id = history.chapter_id LEFT JOIN merged as ME ON ME.manga_id = chapters.manga_id + WHERE excluded_scanlators.scanlator IS NULL GROUP BY ME.merge_id ) AS C ON ME.merge_id = C.merge_id diff --git a/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt index 1b4c08b74..8daeb7112 100644 --- a/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/chapter/ChapterRepositoryImpl.kt @@ -2,6 +2,7 @@ package tachiyomi.data.chapter import kotlinx.coroutines.flow.Flow import logcat.LogPriority +import tachiyomi.core.util.lang.toLong import tachiyomi.core.util.system.logcat import tachiyomi.data.DatabaseHandler import tachiyomi.domain.chapter.model.Chapter @@ -76,8 +77,22 @@ class ChapterRepositoryImpl( } } - override suspend fun getChapterByMangaId(mangaId: Long): List { - return handler.awaitList { chaptersQueries.getChaptersByMangaId(mangaId, ChapterMapper::mapChapter) } + override suspend fun getChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean): List { + return handler.awaitList { + chaptersQueries.getChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ChapterMapper::mapChapter) + } + } + + override suspend fun getScanlatorsByMangaId(mangaId: Long): List { + return handler.awaitList { + chaptersQueries.getScanlatorsByMangaId(mangaId) { it.orEmpty() } + } + } + + override fun getScanlatorsByMangaIdAsFlow(mangaId: Long): Flow> { + return handler.subscribeToList { + chaptersQueries.getScanlatorsByMangaId(mangaId) { it.orEmpty() } + } } override suspend fun getBookmarkedChaptersByMangaId(mangaId: Long): List { @@ -93,12 +108,9 @@ class ChapterRepositoryImpl( return handler.awaitOneOrNull { chaptersQueries.getChapterById(id, ChapterMapper::mapChapter) } } - override suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow> { + override suspend fun getChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean): Flow> { return handler.subscribeToList { - chaptersQueries.getChaptersByMangaId( - mangaId, - ChapterMapper::mapChapter, - ) + chaptersQueries.getChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ChapterMapper::mapChapter) } } @@ -117,13 +129,13 @@ class ChapterRepositoryImpl( return handler.awaitList { chaptersQueries.getChapterByUrl(url, ChapterMapper::mapChapter) } } - override suspend fun getMergedChapterByMangaId(mangaId: Long): List { - return handler.awaitList { chaptersQueries.getMergedChaptersByMangaId(mangaId, ChapterMapper::mapChapter) } + override suspend fun getMergedChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean): List { + return handler.awaitList { chaptersQueries.getMergedChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ChapterMapper::mapChapter) } } - override suspend fun getMergedChapterByMangaIdAsFlow(mangaId: Long): Flow> { + override suspend fun getMergedChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean): Flow> { return handler.subscribeToList { - chaptersQueries.getMergedChaptersByMangaId(mangaId, ChapterMapper::mapChapter) + chaptersQueries.getMergedChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ChapterMapper::mapChapter) } } // SY <-- diff --git a/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt b/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt index d6f1f4655..2ca2a4831 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaMapper.kt @@ -25,7 +25,10 @@ object MangaMapper { chapterFlags: Long, coverLastModified: Long, dateAdded: Long, - filteredScanlators: List?, + // SY --> + @Suppress("UNUSED_PARAMETER") + filteredScanlators: String?, + // SY <-- updateStrategy: UpdateStrategy, calculateInterval: Long, lastModifiedAt: Long, @@ -53,9 +56,6 @@ object MangaMapper { thumbnailUrl = thumbnailUrl, updateStrategy = updateStrategy, initialized = initialized, - // SY --> - filteredScanlators = filteredScanlators, - // SY <-- lastModifiedAt = lastModifiedAt, favoriteModifiedAt = favoriteModifiedAt, ) @@ -79,7 +79,10 @@ object MangaMapper { chapterFlags: Long, coverLastModified: Long, dateAdded: Long, - filteredScanlators: List?, + // SY --> + @Suppress("UNUSED_PARAMETER") + filteredScanlators: String?, + // SY <-- updateStrategy: UpdateStrategy, calculateInterval: Long, lastModifiedAt: Long, @@ -112,7 +115,7 @@ object MangaMapper { coverLastModified, dateAdded, // SY --> - filteredScanlators, + null, // SY <-- updateStrategy, calculateInterval, @@ -150,7 +153,6 @@ object MangaMapper { thumbnailUrl = libraryView.thumbnail_url, updateStrategy = libraryView.update_strategy, initialized = libraryView.initialized, - filteredScanlators = libraryView.filtered_scanlators, fetchInterval = libraryView.calculate_interval.toInt(), lastModifiedAt = libraryView.last_modified_at, favoriteModifiedAt = libraryView.favorite_modified_at, diff --git a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt index d59453c33..2687d0d7d 100644 --- a/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt +++ b/data/src/main/java/tachiyomi/data/manga/MangaRepositoryImpl.kt @@ -6,7 +6,6 @@ import logcat.LogPriority import tachiyomi.core.util.system.logcat import tachiyomi.data.AndroidDatabaseHandler import tachiyomi.data.DatabaseHandler -import tachiyomi.data.StringListAndColumnAdapter import tachiyomi.data.StringListColumnAdapter import tachiyomi.data.UpdateStrategyColumnAdapter import tachiyomi.domain.library.model.LibraryManga @@ -119,9 +118,6 @@ class MangaRepositoryImpl( chapterFlags = manga.chapterFlags, coverLastModified = manga.coverLastModified, dateAdded = manga.dateAdded, - // SY --> - filteredScanlators = manga.filteredScanlators, - // SY <-- updateStrategy = manga.updateStrategy, ) mangasQueries.selectLastInsertedRowId() @@ -170,9 +166,6 @@ class MangaRepositoryImpl( chapterFlags = value.chapterFlags, coverLastModified = value.coverLastModified, dateAdded = value.dateAdded, - // SY --> - filteredScanlators = value.filteredScanlators?.let(StringListAndColumnAdapter::encode), - // SY <-- mangaId = value.id, updateStrategy = value.updateStrategy?.let(UpdateStrategyColumnAdapter::encode), ) diff --git a/data/src/main/sqldelight/tachiyomi/data/chapters.sq b/data/src/main/sqldelight/tachiyomi/data/chapters.sq index 5b0038174..cdc43c93d 100644 --- a/data/src/main/sqldelight/tachiyomi/data/chapters.sq +++ b/data/src/main/sqldelight/tachiyomi/data/chapters.sq @@ -36,7 +36,19 @@ FROM chapters WHERE _id = :id; getChaptersByMangaId: -SELECT * +SELECT C.* +FROM chapters C +LEFT JOIN excluded_scanlators ES +ON C.manga_id = ES.manga_id +AND C.scanlator = ES.scanlator +WHERE C.manga_id = :mangaId +AND ( + :applyScanlatorFilter = 0 + OR ES.scanlator IS NULL +); + +getScanlatorsByMangaId: +SELECT scanlator FROM chapters WHERE manga_id = :mangaId; @@ -58,12 +70,20 @@ WHERE url = :chapterUrl AND manga_id = :mangaId; getMergedChaptersByMangaId: -SELECT chapters.* -FROM ( - SELECT manga_id FROM merged WHERE merge_id = ? +SELECT C.* +FROM chapters C +JOIN ( + SELECT manga_id FROM merged WHERE merge_id = :mangaId ) AS M -JOIN chapters -ON chapters.manga_id = M.manga_id; +ON C.manga_id = M.manga_id +LEFT JOIN excluded_scanlators ES +ON C.manga_id = ES.manga_id +AND C.scanlator = ES.scanlator +WHERE C.manga_id = :mangaId +AND ( + :applyScanlatorFilter = 0 + OR ES.scanlator IS NULL +); removeChaptersWithIds: DELETE FROM chapters diff --git a/data/src/main/sqldelight/tachiyomi/data/eh.sq b/data/src/main/sqldelight/tachiyomi/data/eh.sq index 412c8895b..3b4bf9220 100644 --- a/data/src/main/sqldelight/tachiyomi/data/eh.sq +++ b/data/src/main/sqldelight/tachiyomi/data/eh.sq @@ -9,10 +9,6 @@ WHERE source = :oldId; getChaptersByMangaIds: SELECT * FROM chapters WHERE manga_id IN :mangaIds; -resetFilteredScanlatorsForAllManga: -UPDATE mangas -SET filtered_scanlators = NULL; - migrateAllNhentaiToOtherLang: UPDATE mangas SET source = :nh diff --git a/data/src/main/sqldelight/tachiyomi/data/excluded_scanlators.sq b/data/src/main/sqldelight/tachiyomi/data/excluded_scanlators.sq new file mode 100644 index 000000000..2af2f4199 --- /dev/null +++ b/data/src/main/sqldelight/tachiyomi/data/excluded_scanlators.sq @@ -0,0 +1,22 @@ +CREATE TABLE excluded_scanlators( + manga_id INTEGER NOT NULL, + scanlator TEXT NOT NULL, + FOREIGN KEY(manga_id) REFERENCES mangas (_id) + ON DELETE CASCADE +); + +CREATE INDEX excluded_scanlators_manga_id_index ON excluded_scanlators(manga_id); + +insert: +INSERT INTO excluded_scanlators(manga_id, scanlator) +VALUES (:mangaId, :scanlator); + +remove: +DELETE FROM excluded_scanlators +WHERE manga_id = :mangaId +AND scanlator IN :scanlators; + +getExcludedScanlatorsByMangaId: +SELECT scanlator +FROM excluded_scanlators +WHERE manga_id = :mangaId; diff --git a/data/src/main/sqldelight/tachiyomi/data/mangas.sq b/data/src/main/sqldelight/tachiyomi/data/mangas.sq index 5d1425785..54f1d4dc0 100644 --- a/data/src/main/sqldelight/tachiyomi/data/mangas.sq +++ b/data/src/main/sqldelight/tachiyomi/data/mangas.sq @@ -22,7 +22,7 @@ CREATE TABLE mangas( chapter_flags INTEGER NOT NULL, cover_last_modified INTEGER NOT NULL, date_added INTEGER NOT NULL, - filtered_scanlators TEXT AS List, + filtered_scanlators TEXT, update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0, calculate_interval INTEGER DEFAULT 0 NOT NULL, last_modified_at INTEGER NOT NULL DEFAULT 0, @@ -131,8 +131,8 @@ WHERE favorite = 0 AND source IN :sourceIdsAND AND _id NOT IN ( ); insert: -INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, filtered_scanlators, update_strategy, calculate_interval, last_modified_at) -VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :filteredScanlators, :updateStrategy, :calculateInterval, strftime('%s', 'now')); +INSERT INTO mangas(source, url, artist, author, description, genre, title, status, thumbnail_url, favorite, last_update, next_update, initialized, viewer, chapter_flags, cover_last_modified, date_added, update_strategy, calculate_interval, last_modified_at) +VALUES (:source, :url, :artist, :author, :description, :genre, :title, :status, :thumbnailUrl, :favorite, :lastUpdate, :nextUpdate, :initialized, :viewerFlags, :chapterFlags, :coverLastModified, :dateAdded, :updateStrategy, :calculateInterval, strftime('%s', 'now')); update: UPDATE mangas SET @@ -153,7 +153,6 @@ UPDATE mangas SET chapter_flags = coalesce(:chapterFlags, chapter_flags), cover_last_modified = coalesce(:coverLastModified, cover_last_modified), date_added = coalesce(:dateAdded, date_added), - filtered_scanlators = coalesce(:filteredScanlators, filtered_scanlators), update_strategy = coalesce(:updateStrategy, update_strategy), calculate_interval = coalesce(:calculateInterval, calculate_interval) WHERE _id = :mangaId; diff --git a/data/src/main/sqldelight/tachiyomi/migrations/29.sqm b/data/src/main/sqldelight/tachiyomi/migrations/29.sqm new file mode 100644 index 000000000..7e6c0adcb --- /dev/null +++ b/data/src/main/sqldelight/tachiyomi/migrations/29.sqm @@ -0,0 +1,44 @@ +CREATE TABLE excluded_scanlators( + manga_id INTEGER NOT NULL, + scanlator TEXT NOT NULL, + FOREIGN KEY(manga_id) REFERENCES mangas (_id) + ON DELETE CASCADE +); + +CREATE INDEX excluded_scanlators_manga_id_index ON excluded_scanlators(manga_id); + +DROP VIEW IF EXISTS libraryView; + +CREATE VIEW libraryView AS +SELECT + M.*, + coalesce(C.total, 0) AS totalCount, + coalesce(C.readCount, 0) AS readCount, + coalesce(C.latestUpload, 0) AS latestUpload, + coalesce(C.fetchedAt, 0) AS chapterFetchedAt, + coalesce(C.lastRead, 0) AS lastRead, + coalesce(C.bookmarkCount, 0) AS bookmarkCount, + coalesce(MC.category_id, 0) AS category +FROM mangas M +LEFT JOIN( + SELECT + chapters.manga_id, + count(*) AS total, + sum(read) AS readCount, + coalesce(max(chapters.date_upload), 0) AS latestUpload, + coalesce(max(history.last_read), 0) AS lastRead, + coalesce(max(chapters.date_fetch), 0) AS fetchedAt, + sum(chapters.bookmark) AS bookmarkCount + FROM chapters + LEFT JOIN excluded_scanlators + ON chapters.manga_id = excluded_scanlators.manga_id + AND chapters.scanlator = excluded_scanlators.scanlator + LEFT JOIN history + ON chapters._id = history.chapter_id + WHERE excluded_scanlators.scanlator IS NULL + GROUP BY chapters.manga_id +) AS C +ON M._id = C.manga_id +LEFT JOIN mangas_categories AS MC +ON MC.manga_id = M._id +WHERE M.favorite = 1; \ No newline at end of file diff --git a/data/src/main/sqldelight/tachiyomi/view/libraryView.sq b/data/src/main/sqldelight/tachiyomi/view/libraryView.sq index fe88aeed5..1021c544f 100644 --- a/data/src/main/sqldelight/tachiyomi/view/libraryView.sq +++ b/data/src/main/sqldelight/tachiyomi/view/libraryView.sq @@ -19,8 +19,12 @@ LEFT JOIN( coalesce(max(chapters.date_fetch), 0) AS fetchedAt, sum(chapters.bookmark) AS bookmarkCount FROM chapters + LEFT JOIN excluded_scanlators + ON chapters.manga_id = excluded_scanlators.manga_id + AND chapters.scanlator = excluded_scanlators.scanlator LEFT JOIN history ON chapters._id = history.chapter_id + WHERE excluded_scanlators.scanlator IS NULL GROUP BY chapters.manga_id ) AS C ON M._id = C.manga_id diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt index 6dcc0d32b..66dab15c7 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetChaptersByMangaId.kt @@ -9,9 +9,9 @@ class GetChaptersByMangaId( private val chapterRepository: ChapterRepository, ) { - suspend fun await(mangaId: Long): List { + suspend fun await(mangaId: Long, applyScanlatorFilter: Boolean = false): List { return try { - chapterRepository.getChapterByMangaId(mangaId) + chapterRepository.getChapterByMangaId(mangaId, applyScanlatorFilter) } catch (e: Exception) { logcat(LogPriority.ERROR, e) emptyList() diff --git a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetMergedChaptersByMangaId.kt b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetMergedChaptersByMangaId.kt index e95315f8f..592bc1c77 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetMergedChaptersByMangaId.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/interactor/GetMergedChaptersByMangaId.kt @@ -16,13 +16,21 @@ class GetMergedChaptersByMangaId( private val getMergedReferencesById: GetMergedReferencesById, ) { - suspend fun await(mangaId: Long, dedupe: Boolean = true): List { - return transformMergedChapters(getMergedReferencesById.await(mangaId), getFromDatabase(mangaId), dedupe) + suspend fun await( + mangaId: Long, + dedupe: Boolean = true, + applyScanlatorFilter: Boolean = false, + ): List { + return transformMergedChapters(getMergedReferencesById.await(mangaId), getFromDatabase(mangaId, applyScanlatorFilter), dedupe) } - suspend fun subscribe(mangaId: Long, dedupe: Boolean = true): Flow> { + suspend fun subscribe( + mangaId: Long, + dedupe: Boolean = true, + applyScanlatorFilter: Boolean = false, + ): Flow> { return try { - chapterRepository.getMergedChapterByMangaIdAsFlow(mangaId) + chapterRepository.getMergedChapterByMangaIdAsFlow(mangaId, applyScanlatorFilter) .combine(getMergedReferencesById.subscribe(mangaId)) { chapters, references -> transformMergedChapters(references, chapters, dedupe) } @@ -32,16 +40,19 @@ class GetMergedChaptersByMangaId( } } - private suspend fun getFromDatabase(mangaId: Long): List { + private suspend fun getFromDatabase( + mangaId: Long, + applyScanlatorFilter: Boolean = false, + ): List { return try { - chapterRepository.getMergedChapterByMangaId(mangaId) + chapterRepository.getMergedChapterByMangaId(mangaId, applyScanlatorFilter) } catch (e: Exception) { logcat(LogPriority.ERROR, e) emptyList() } } - fun transformMergedChapters( + private fun transformMergedChapters( mangaReferences: List, chapterList: List, dedupe: Boolean, diff --git a/domain/src/main/java/tachiyomi/domain/chapter/repository/ChapterRepository.kt b/domain/src/main/java/tachiyomi/domain/chapter/repository/ChapterRepository.kt index dcd5690aa..e7a8658bf 100644 --- a/domain/src/main/java/tachiyomi/domain/chapter/repository/ChapterRepository.kt +++ b/domain/src/main/java/tachiyomi/domain/chapter/repository/ChapterRepository.kt @@ -14,21 +14,25 @@ interface ChapterRepository { suspend fun removeChaptersWithIds(chapterIds: List) - suspend fun getChapterByMangaId(mangaId: Long): List + suspend fun getChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean = false): List + + suspend fun getScanlatorsByMangaId(mangaId: Long): List + + fun getScanlatorsByMangaIdAsFlow(mangaId: Long): Flow> suspend fun getBookmarkedChaptersByMangaId(mangaId: Long): List suspend fun getChapterById(id: Long): Chapter? - suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow> + suspend fun getChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean = false): Flow> suspend fun getChapterByUrlAndMangaId(url: String, mangaId: Long): Chapter? // SY --> suspend fun getChapterByUrl(url: String): List - suspend fun getMergedChapterByMangaId(mangaId: Long): List + suspend fun getMergedChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean = false): List - suspend fun getMergedChapterByMangaIdAsFlow(mangaId: Long): Flow> + suspend fun getMergedChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean = false): Flow> // SY <-- } diff --git a/domain/src/main/java/tachiyomi/domain/history/interactor/GetNextChapters.kt b/domain/src/main/java/tachiyomi/domain/history/interactor/GetNextChapters.kt index d30152178..be10e688f 100644 --- a/domain/src/main/java/tachiyomi/domain/history/interactor/GetNextChapters.kt +++ b/domain/src/main/java/tachiyomi/domain/history/interactor/GetNextChapters.kt @@ -29,7 +29,7 @@ class GetNextChapters( // SY --> if (manga.source == MERGED_SOURCE_ID) { - val chapters = getMergedChaptersByMangaId.await(mangaId) + val chapters = getMergedChaptersByMangaId.await(mangaId, applyScanlatorFilter = true) .sortedWith(getChapterSort(manga, sortDescending = false)) return if (onlyUnread) { @@ -39,7 +39,7 @@ class GetNextChapters( } } if (manga.isEhBasedManga()) { - val chapters = getChaptersByMangaId.await(mangaId) + val chapters = getChaptersByMangaId.await(mangaId, applyScanlatorFilter = true) .sortedWith(getChapterSort(manga, sortDescending = false)) return if (onlyUnread) { @@ -50,7 +50,7 @@ class GetNextChapters( } // SY <-- - val chapters = getChaptersByMangaId.await(mangaId) + val chapters = getChaptersByMangaId.await(mangaId, applyScanlatorFilter = true) .sortedWith(getChapterSort(manga, sortDescending = false)) return if (onlyUnread) { diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt index 308ed8bf6..0a9124d16 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/FetchInterval.kt @@ -24,7 +24,7 @@ class FetchInterval( } else { window } - val chapters = getChaptersByMangaId.await(manga.id) + val chapters = getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval( chapters, dateTime.zone, diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaWithChapters.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaWithChapters.kt index 189fe5c1a..4fddd8140 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaWithChapters.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/interactor/GetMangaWithChapters.kt @@ -12,10 +12,10 @@ class GetMangaWithChapters( private val chapterRepository: ChapterRepository, ) { - suspend fun subscribe(id: Long): Flow>> { + suspend fun subscribe(id: Long, applyScanlatorFilter: Boolean = false): Flow>> { return combine( mangaRepository.getMangaByIdAsFlow(id), - chapterRepository.getChapterByMangaIdAsFlow(id), + chapterRepository.getChapterByMangaIdAsFlow(id, applyScanlatorFilter), ) { manga, chapters -> Pair(manga, chapters) } @@ -25,7 +25,7 @@ class GetMangaWithChapters( return mangaRepository.getMangaById(id) } - suspend fun awaitChapters(id: Long): List { - return chapterRepository.getChapterByMangaId(id) + suspend fun awaitChapters(id: Long, applyScanlatorFilter: Boolean = false): List { + return chapterRepository.getChapterByMangaId(id, applyScanlatorFilter) } } diff --git a/domain/src/main/java/tachiyomi/domain/manga/interactor/SetMangaFilteredScanlators.kt b/domain/src/main/java/tachiyomi/domain/manga/interactor/SetMangaFilteredScanlators.kt deleted file mode 100644 index 0ac31ba77..000000000 --- a/domain/src/main/java/tachiyomi/domain/manga/interactor/SetMangaFilteredScanlators.kt +++ /dev/null @@ -1,17 +0,0 @@ -package tachiyomi.domain.manga.interactor - -import tachiyomi.domain.manga.model.Manga -import tachiyomi.domain.manga.model.MangaUpdate -import tachiyomi.domain.manga.repository.MangaRepository - -class SetMangaFilteredScanlators(private val mangaRepository: MangaRepository) { - - suspend fun awaitSetFilteredScanlators(manga: Manga, filteredScanlators: List): Boolean { - return mangaRepository.update( - MangaUpdate( - id = manga.id, - filteredScanlators = filteredScanlators, - ), - ) - } -} diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt index 7e7d52244..163617d64 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/Manga.kt @@ -31,9 +31,6 @@ data class Manga( val initialized: Boolean, val lastModifiedAt: Long, val favoriteModifiedAt: Long?, - // SY --> - val filteredScanlators: List?, - // SY <-- ) : Serializable { // SY --> @@ -152,9 +149,6 @@ data class Manga( initialized = false, lastModifiedAt = 0L, favoriteModifiedAt = null, - // SY --> - filteredScanlators = null, - // SY <-- ) // SY --> diff --git a/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt b/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt index 873d16471..f434445aa 100644 --- a/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt +++ b/domain/src/main/java/tachiyomi/domain/manga/model/MangaUpdate.kt @@ -52,8 +52,5 @@ fun Manga.toMangaUpdate(): MangaUpdate { thumbnailUrl = thumbnailUrl, updateStrategy = updateStrategy, initialized = initialized, - // SY --> - filteredScanlators = filteredScanlators, - // SY <-- ) } diff --git a/i18n/src/main/res/values-fr/strings_sy.xml b/i18n/src/main/res/values-fr/strings_sy.xml index ebf7c5525..bbce37fcf 100644 --- a/i18n/src/main/res/values-fr/strings_sy.xml +++ b/i18n/src/main/res/values-fr/strings_sy.xml @@ -434,7 +434,6 @@ Titre court Type de fichier d\'image de couverture Type de fichier d\'image miniature - Scanlator Url Uploader en majuscule Uploader diff --git a/i18n/src/main/res/values-in/strings_sy.xml b/i18n/src/main/res/values-in/strings_sy.xml index 81c7f62ee..759de04e4 100644 --- a/i18n/src/main/res/values-in/strings_sy.xml +++ b/i18n/src/main/res/values-in/strings_sy.xml @@ -545,7 +545,6 @@ Short Title Cover image file type Thumbnail image file type - Scanlator Url Uploader Capitalized Uploader diff --git a/i18n/src/main/res/values-pt-rBR/strings_sy.xml b/i18n/src/main/res/values-pt-rBR/strings_sy.xml index 4954e5823..3d15782e6 100644 --- a/i18n/src/main/res/values-pt-rBR/strings_sy.xml +++ b/i18n/src/main/res/values-pt-rBR/strings_sy.xml @@ -515,7 +515,6 @@ Título curto Tipo arq. da capa Tipo arq. miniatura - Scanlator Url Uploader Capitalizado Uploader diff --git a/i18n/src/main/res/values-ru/strings_sy.xml b/i18n/src/main/res/values-ru/strings_sy.xml index a2db85bca..f338c29fd 100644 --- a/i18n/src/main/res/values-ru/strings_sy.xml +++ b/i18n/src/main/res/values-ru/strings_sy.xml @@ -616,7 +616,6 @@ Краткое название Тип изображения обложки Тип изображения миниатюры - Переводчик URL-адрес Имя загрузчика с заглавной буквы Имя загрузчика diff --git a/i18n/src/main/res/values-zh-rCN/strings_sy.xml b/i18n/src/main/res/values-zh-rCN/strings_sy.xml index 5fdaf16c9..b0a57d53f 100644 --- a/i18n/src/main/res/values-zh-rCN/strings_sy.xml +++ b/i18n/src/main/res/values-zh-rCN/strings_sy.xml @@ -582,7 +582,6 @@ 短标题 转换图像类型 缓存图像类型 - 扫译组 上传者名字 上传者 评分字符串 diff --git a/i18n/src/main/res/values/strings.xml b/i18n/src/main/res/values/strings.xml index dada03e0d..2ed31f068 100755 --- a/i18n/src/main/res/values/strings.xml +++ b/i18n/src/main/res/values/strings.xml @@ -14,6 +14,7 @@ Tracking Delete downloaded History + Scanlator More @@ -702,6 +703,8 @@ Set as default No chapters found Are you sure? + Exclude scanlators + No scanlators found Tracking diff --git a/i18n/src/main/res/values/strings_sy.xml b/i18n/src/main/res/values/strings_sy.xml index ec9c9a850..69d1145ec 100644 --- a/i18n/src/main/res/values/strings_sy.xml +++ b/i18n/src/main/res/values/strings_sy.xml @@ -613,7 +613,6 @@ Short Title Cover image file type Thumbnail image file type - Scanlator Url Uploader Capitalized Uploader