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
This commit is contained in:
parent
94fa45597d
commit
c8909961c0
@ -26,7 +26,7 @@ android {
|
||||
defaultConfig {
|
||||
applicationId = "eu.kanade.tachiyomi.sy"
|
||||
|
||||
versionCode = 58
|
||||
versionCode = 59
|
||||
versionName = "1.9.4"
|
||||
|
||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||
|
@ -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<ReleaseService> { 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<HistoryRepository> { HistoryRepositoryImpl(get()) }
|
||||
addFactory { GetHistory(get()) }
|
||||
|
@ -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()) }
|
||||
|
@ -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<String>.cleanupAvailableScanlators(): Set<String> {
|
||||
return mapNotNull { it.ifBlank { null } }.toSet()
|
||||
}
|
||||
|
||||
suspend fun await(mangaId: Long): Set<String> {
|
||||
return repository.getScanlatorsByMangaId(mangaId)
|
||||
.cleanupAvailableScanlators()
|
||||
}
|
||||
|
||||
fun subscribe(mangaId: Long): Flow<Set<String>> {
|
||||
return repository.getScanlatorsByMangaIdAsFlow(mangaId)
|
||||
.map { it.cleanupAvailableScanlators() }
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<Chapter>.applyFilters(
|
||||
manga: Manga,
|
||||
downloadManager: DownloadManager/* SY --> */,
|
||||
downloadManager: DownloadManager,/* SY --> */
|
||||
mergedManga: Map<Long, Manga>, /* SY <-- */
|
||||
): List<Chapter> {
|
||||
val isLocalManga = manga.isLocal()
|
||||
@ -41,14 +40,6 @@ fun List<Chapter>.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<ChapterList.Item>.applyFilters(manga: Manga): Sequence<ChapterList.Item
|
||||
.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) }
|
||||
}
|
||||
|
@ -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<String> {
|
||||
return handler.awaitList {
|
||||
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(mangaId)
|
||||
}
|
||||
.toSet()
|
||||
}
|
||||
|
||||
fun subscribe(mangaId: Long): Flow<Set<String>> {
|
||||
return handler.subscribeToList {
|
||||
excluded_scanlatorsQueries.getExcludedScanlatorsByMangaId(mangaId)
|
||||
}
|
||||
.map { it.toSet() }
|
||||
}
|
||||
}
|
@ -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<String>) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@ -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<BasePreferences>().downloadedOnly().get()
|
||||
|
@ -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(
|
||||
|
@ -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,
|
||||
|
@ -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<String>,
|
||||
excludedScanlators: Set<String>,
|
||||
onDismissRequest: () -> Unit,
|
||||
onConfirm: (Set<String>) -> 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))
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
@ -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 <--
|
||||
),
|
||||
)
|
||||
}
|
||||
|
@ -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<BackupChapter>::isEmpty)
|
||||
?.let { mangaObject.chapters = it }
|
||||
}
|
||||
|
||||
// Check if user wants category information in backup
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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 <--
|
||||
}
|
||||
|
@ -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?) {
|
||||
|
@ -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<Chapter>): List<String> {
|
||||
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<MetadataSource<*, *>>()?.metaClass
|
||||
@ -1299,15 +1315,6 @@ class MangaScreenModel(
|
||||
}
|
||||
}
|
||||
|
||||
// SY -->
|
||||
fun setScanlatorFilter(filteredScanlators: List<String>) {
|
||||
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<String>) {
|
||||
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<ChapterList.Item>,
|
||||
val availableScanlators: Set<String>,
|
||||
val excludedScanlators: Set<String>,
|
||||
val trackItems: List<TrackItem> = 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<String>,
|
||||
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) }
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -27,16 +27,3 @@ object UpdateStrategyColumnAdapter : ColumnAdapter<UpdateStrategy, Long> {
|
||||
|
||||
override fun encode(value: UpdateStrategy): Long = value.ordinal.toLong()
|
||||
}
|
||||
|
||||
// SY -->
|
||||
private const val LIST_OF_STRINGS_AND_SEPARATOR = " & "
|
||||
object StringListAndColumnAdapter : ColumnAdapter<List<String>, String> {
|
||||
override fun decode(databaseValue: String) =
|
||||
if (databaseValue.isEmpty()) {
|
||||
emptyList()
|
||||
} else {
|
||||
databaseValue.split(LIST_OF_STRINGS_AND_SEPARATOR)
|
||||
}
|
||||
override fun encode(value: List<String>) = value.joinToString(separator = LIST_OF_STRINGS_AND_SEPARATOR)
|
||||
}
|
||||
// SY <--
|
||||
|
@ -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
|
||||
|
@ -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<Chapter> {
|
||||
return handler.awaitList { chaptersQueries.getChaptersByMangaId(mangaId, ChapterMapper::mapChapter) }
|
||||
override suspend fun getChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean): List<Chapter> {
|
||||
return handler.awaitList {
|
||||
chaptersQueries.getChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ChapterMapper::mapChapter)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getScanlatorsByMangaId(mangaId: Long): List<String> {
|
||||
return handler.awaitList {
|
||||
chaptersQueries.getScanlatorsByMangaId(mangaId) { it.orEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getScanlatorsByMangaIdAsFlow(mangaId: Long): Flow<List<String>> {
|
||||
return handler.subscribeToList {
|
||||
chaptersQueries.getScanlatorsByMangaId(mangaId) { it.orEmpty() }
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getBookmarkedChaptersByMangaId(mangaId: Long): List<Chapter> {
|
||||
@ -93,12 +108,9 @@ class ChapterRepositoryImpl(
|
||||
return handler.awaitOneOrNull { chaptersQueries.getChapterById(id, ChapterMapper::mapChapter) }
|
||||
}
|
||||
|
||||
override suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>> {
|
||||
override suspend fun getChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean): Flow<List<Chapter>> {
|
||||
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<Chapter> {
|
||||
return handler.awaitList { chaptersQueries.getMergedChaptersByMangaId(mangaId, ChapterMapper::mapChapter) }
|
||||
override suspend fun getMergedChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean): List<Chapter> {
|
||||
return handler.awaitList { chaptersQueries.getMergedChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ChapterMapper::mapChapter) }
|
||||
}
|
||||
|
||||
override suspend fun getMergedChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>> {
|
||||
override suspend fun getMergedChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean): Flow<List<Chapter>> {
|
||||
return handler.subscribeToList {
|
||||
chaptersQueries.getMergedChaptersByMangaId(mangaId, ChapterMapper::mapChapter)
|
||||
chaptersQueries.getMergedChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ChapterMapper::mapChapter)
|
||||
}
|
||||
}
|
||||
// SY <--
|
||||
|
@ -25,7 +25,10 @@ object MangaMapper {
|
||||
chapterFlags: Long,
|
||||
coverLastModified: Long,
|
||||
dateAdded: Long,
|
||||
filteredScanlators: List<String>?,
|
||||
// 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<String>?,
|
||||
// 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,
|
||||
|
@ -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),
|
||||
)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
@ -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<String>,
|
||||
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;
|
||||
|
44
data/src/main/sqldelight/tachiyomi/migrations/29.sqm
Normal file
44
data/src/main/sqldelight/tachiyomi/migrations/29.sqm
Normal file
@ -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;
|
@ -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
|
||||
|
@ -9,9 +9,9 @@ class GetChaptersByMangaId(
|
||||
private val chapterRepository: ChapterRepository,
|
||||
) {
|
||||
|
||||
suspend fun await(mangaId: Long): List<Chapter> {
|
||||
suspend fun await(mangaId: Long, applyScanlatorFilter: Boolean = false): List<Chapter> {
|
||||
return try {
|
||||
chapterRepository.getChapterByMangaId(mangaId)
|
||||
chapterRepository.getChapterByMangaId(mangaId, applyScanlatorFilter)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
emptyList()
|
||||
|
@ -16,13 +16,21 @@ class GetMergedChaptersByMangaId(
|
||||
private val getMergedReferencesById: GetMergedReferencesById,
|
||||
) {
|
||||
|
||||
suspend fun await(mangaId: Long, dedupe: Boolean = true): List<Chapter> {
|
||||
return transformMergedChapters(getMergedReferencesById.await(mangaId), getFromDatabase(mangaId), dedupe)
|
||||
suspend fun await(
|
||||
mangaId: Long,
|
||||
dedupe: Boolean = true,
|
||||
applyScanlatorFilter: Boolean = false,
|
||||
): List<Chapter> {
|
||||
return transformMergedChapters(getMergedReferencesById.await(mangaId), getFromDatabase(mangaId, applyScanlatorFilter), dedupe)
|
||||
}
|
||||
|
||||
suspend fun subscribe(mangaId: Long, dedupe: Boolean = true): Flow<List<Chapter>> {
|
||||
suspend fun subscribe(
|
||||
mangaId: Long,
|
||||
dedupe: Boolean = true,
|
||||
applyScanlatorFilter: Boolean = false,
|
||||
): Flow<List<Chapter>> {
|
||||
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<Chapter> {
|
||||
private suspend fun getFromDatabase(
|
||||
mangaId: Long,
|
||||
applyScanlatorFilter: Boolean = false,
|
||||
): List<Chapter> {
|
||||
return try {
|
||||
chapterRepository.getMergedChapterByMangaId(mangaId)
|
||||
chapterRepository.getMergedChapterByMangaId(mangaId, applyScanlatorFilter)
|
||||
} catch (e: Exception) {
|
||||
logcat(LogPriority.ERROR, e)
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
fun transformMergedChapters(
|
||||
private fun transformMergedChapters(
|
||||
mangaReferences: List<MergedMangaReference>,
|
||||
chapterList: List<Chapter>,
|
||||
dedupe: Boolean,
|
||||
|
@ -14,21 +14,25 @@ interface ChapterRepository {
|
||||
|
||||
suspend fun removeChaptersWithIds(chapterIds: List<Long>)
|
||||
|
||||
suspend fun getChapterByMangaId(mangaId: Long): List<Chapter>
|
||||
suspend fun getChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean = false): List<Chapter>
|
||||
|
||||
suspend fun getScanlatorsByMangaId(mangaId: Long): List<String>
|
||||
|
||||
fun getScanlatorsByMangaIdAsFlow(mangaId: Long): Flow<List<String>>
|
||||
|
||||
suspend fun getBookmarkedChaptersByMangaId(mangaId: Long): List<Chapter>
|
||||
|
||||
suspend fun getChapterById(id: Long): Chapter?
|
||||
|
||||
suspend fun getChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>>
|
||||
suspend fun getChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean = false): Flow<List<Chapter>>
|
||||
|
||||
suspend fun getChapterByUrlAndMangaId(url: String, mangaId: Long): Chapter?
|
||||
|
||||
// SY -->
|
||||
suspend fun getChapterByUrl(url: String): List<Chapter>
|
||||
|
||||
suspend fun getMergedChapterByMangaId(mangaId: Long): List<Chapter>
|
||||
suspend fun getMergedChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean = false): List<Chapter>
|
||||
|
||||
suspend fun getMergedChapterByMangaIdAsFlow(mangaId: Long): Flow<List<Chapter>>
|
||||
suspend fun getMergedChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean = false): Flow<List<Chapter>>
|
||||
// SY <--
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -12,10 +12,10 @@ class GetMangaWithChapters(
|
||||
private val chapterRepository: ChapterRepository,
|
||||
) {
|
||||
|
||||
suspend fun subscribe(id: Long): Flow<Pair<Manga, List<Chapter>>> {
|
||||
suspend fun subscribe(id: Long, applyScanlatorFilter: Boolean = false): Flow<Pair<Manga, List<Chapter>>> {
|
||||
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<Chapter> {
|
||||
return chapterRepository.getChapterByMangaId(id)
|
||||
suspend fun awaitChapters(id: Long, applyScanlatorFilter: Boolean = false): List<Chapter> {
|
||||
return chapterRepository.getChapterByMangaId(id, applyScanlatorFilter)
|
||||
}
|
||||
}
|
||||
|
@ -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<String>): Boolean {
|
||||
return mangaRepository.update(
|
||||
MangaUpdate(
|
||||
id = manga.id,
|
||||
filteredScanlators = filteredScanlators,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
@ -31,9 +31,6 @@ data class Manga(
|
||||
val initialized: Boolean,
|
||||
val lastModifiedAt: Long,
|
||||
val favoriteModifiedAt: Long?,
|
||||
// SY -->
|
||||
val filteredScanlators: List<String>?,
|
||||
// SY <--
|
||||
) : Serializable {
|
||||
|
||||
// SY -->
|
||||
@ -152,9 +149,6 @@ data class Manga(
|
||||
initialized = false,
|
||||
lastModifiedAt = 0L,
|
||||
favoriteModifiedAt = null,
|
||||
// SY -->
|
||||
filteredScanlators = null,
|
||||
// SY <--
|
||||
)
|
||||
|
||||
// SY -->
|
||||
|
@ -52,8 +52,5 @@ fun Manga.toMangaUpdate(): MangaUpdate {
|
||||
thumbnailUrl = thumbnailUrl,
|
||||
updateStrategy = updateStrategy,
|
||||
initialized = initialized,
|
||||
// SY -->
|
||||
filteredScanlators = filteredScanlators,
|
||||
// SY <--
|
||||
)
|
||||
}
|
||||
|
@ -434,7 +434,6 @@
|
||||
<string name="short_title">Titre court</string>
|
||||
<string name="cover_image_file_type">Type de fichier d\'image de couverture</string>
|
||||
<string name="thumbnail_image_file_type">Type de fichier d\'image miniature</string>
|
||||
<string name="scanlator">Scanlator</string>
|
||||
<string name="url">Url</string>
|
||||
<string name="uploader_capital">Uploader en majuscule</string>
|
||||
<string name="uploader">Uploader</string>
|
||||
|
@ -545,7 +545,6 @@
|
||||
<string name="short_title">Short Title</string>
|
||||
<string name="cover_image_file_type">Cover image file type</string>
|
||||
<string name="thumbnail_image_file_type">Thumbnail image file type</string>
|
||||
<string name="scanlator">Scanlator</string>
|
||||
<string name="url">Url</string>
|
||||
<string name="uploader_capital">Uploader Capitalized</string>
|
||||
<string name="uploader">Uploader</string>
|
||||
|
@ -515,7 +515,6 @@
|
||||
<string name="short_title">Título curto</string>
|
||||
<string name="cover_image_file_type">Tipo arq. da capa</string>
|
||||
<string name="thumbnail_image_file_type">Tipo arq. miniatura</string>
|
||||
<string name="scanlator">Scanlator</string>
|
||||
<string name="url">Url</string>
|
||||
<string name="uploader_capital">Uploader Capitalizado</string>
|
||||
<string name="uploader">Uploader</string>
|
||||
|
@ -616,7 +616,6 @@
|
||||
<string name="short_title">Краткое название</string>
|
||||
<string name="cover_image_file_type">Тип изображения обложки</string>
|
||||
<string name="thumbnail_image_file_type">Тип изображения миниатюры</string>
|
||||
<string name="scanlator">Переводчик</string>
|
||||
<string name="url">URL-адрес</string>
|
||||
<string name="uploader_capital">Имя загрузчика с заглавной буквы</string>
|
||||
<string name="uploader">Имя загрузчика</string>
|
||||
|
@ -582,7 +582,6 @@
|
||||
<string name="short_title">短标题</string>
|
||||
<string name="cover_image_file_type">转换图像类型</string>
|
||||
<string name="thumbnail_image_file_type">缓存图像类型</string>
|
||||
<string name="scanlator">扫译组</string>
|
||||
<string name="uploader_capital">上传者名字</string>
|
||||
<string name="uploader">上传者</string>
|
||||
<string name="rating_string">评分字符串</string>
|
||||
|
@ -14,6 +14,7 @@
|
||||
<string name="track">Tracking</string>
|
||||
<string name="delete_downloaded">Delete downloaded</string>
|
||||
<string name="history">History</string>
|
||||
<string name="scanlator">Scanlator</string>
|
||||
|
||||
<!-- Screen titles -->
|
||||
<string name="label_more">More</string>
|
||||
@ -702,6 +703,8 @@
|
||||
<string name="set_chapter_settings_as_default">Set as default</string>
|
||||
<string name="no_chapters_error">No chapters found</string>
|
||||
<string name="are_you_sure">Are you sure?</string>
|
||||
<string name="exclude_scanlators">Exclude scanlators</string>
|
||||
<string name="no_scanlators_found">No scanlators found</string>
|
||||
|
||||
<!-- Tracking Screen -->
|
||||
<string name="manga_tracking_tab">Tracking</string>
|
||||
|
@ -613,7 +613,6 @@
|
||||
<string name="short_title">Short Title</string>
|
||||
<string name="cover_image_file_type">Cover image file type</string>
|
||||
<string name="thumbnail_image_file_type">Thumbnail image file type</string>
|
||||
<string name="scanlator">Scanlator</string>
|
||||
<string name="url">Url</string>
|
||||
<string name="uploader_capital">Uploader Capitalized</string>
|
||||
<string name="uploader">Uploader</string>
|
||||
|
Loading…
x
Reference in New Issue
Block a user