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 {
|
defaultConfig {
|
||||||
applicationId = "eu.kanade.tachiyomi.sy"
|
applicationId = "eu.kanade.tachiyomi.sy"
|
||||||
|
|
||||||
versionCode = 58
|
versionCode = 59
|
||||||
versionName = "1.9.4"
|
versionName = "1.9.4"
|
||||||
|
|
||||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
package eu.kanade.domain
|
package eu.kanade.domain
|
||||||
|
|
||||||
|
import eu.kanade.domain.chapter.interactor.GetAvailableScanlators
|
||||||
import eu.kanade.domain.chapter.interactor.SetReadStatus
|
import eu.kanade.domain.chapter.interactor.SetReadStatus
|
||||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
||||||
import eu.kanade.domain.download.interactor.DeleteDownload
|
import eu.kanade.domain.download.interactor.DeleteDownload
|
||||||
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
|
import eu.kanade.domain.extension.interactor.GetExtensionLanguages
|
||||||
import eu.kanade.domain.extension.interactor.GetExtensionSources
|
import eu.kanade.domain.extension.interactor.GetExtensionSources
|
||||||
import eu.kanade.domain.extension.interactor.GetExtensionsByType
|
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.SetMangaViewerFlags
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.source.interactor.GetEnabledSources
|
import eu.kanade.domain.source.interactor.GetEnabledSources
|
||||||
@ -112,6 +115,8 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { NetworkToLocalManga(get()) }
|
addFactory { NetworkToLocalManga(get()) }
|
||||||
addFactory { UpdateManga(get(), get()) }
|
addFactory { UpdateManga(get(), get()) }
|
||||||
addFactory { SetMangaCategories(get()) }
|
addFactory { SetMangaCategories(get()) }
|
||||||
|
addFactory { GetExcludedScanlators(get()) }
|
||||||
|
addFactory { SetExcludedScanlators(get()) }
|
||||||
|
|
||||||
addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) }
|
addSingletonFactory<ReleaseService> { ReleaseServiceImpl(get(), get()) }
|
||||||
addFactory { GetApplicationRelease(get(), get()) }
|
addFactory { GetApplicationRelease(get(), get()) }
|
||||||
@ -133,7 +138,8 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { UpdateChapter(get()) }
|
addFactory { UpdateChapter(get()) }
|
||||||
addFactory { SetReadStatus(get(), get(), get(), get(), get()) }
|
addFactory { SetReadStatus(get(), get(), get(), get(), get()) }
|
||||||
addFactory { ShouldUpdateDbChapter() }
|
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()) }
|
addSingletonFactory<HistoryRepository> { HistoryRepositoryImpl(get()) }
|
||||||
addFactory { GetHistory(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.InsertFlatMetadata
|
||||||
import tachiyomi.domain.manga.interactor.InsertMergedReference
|
import tachiyomi.domain.manga.interactor.InsertMergedReference
|
||||||
import tachiyomi.domain.manga.interactor.SetCustomMangaInfo
|
import tachiyomi.domain.manga.interactor.SetCustomMangaInfo
|
||||||
import tachiyomi.domain.manga.interactor.SetMangaFilteredScanlators
|
|
||||||
import tachiyomi.domain.manga.interactor.UpdateMergedSettings
|
import tachiyomi.domain.manga.interactor.UpdateMergedSettings
|
||||||
import tachiyomi.domain.manga.repository.CustomMangaRepository
|
import tachiyomi.domain.manga.repository.CustomMangaRepository
|
||||||
import tachiyomi.domain.manga.repository.FavoritesEntryRepository
|
import tachiyomi.domain.manga.repository.FavoritesEntryRepository
|
||||||
@ -88,7 +87,6 @@ class SYDomainModule : InjektModule {
|
|||||||
addFactory { GetShowLatest(get()) }
|
addFactory { GetShowLatest(get()) }
|
||||||
addFactory { ToggleExcludeFromDataSaver(get()) }
|
addFactory { ToggleExcludeFromDataSaver(get()) }
|
||||||
addFactory { SetSourceCategories(get()) }
|
addFactory { SetSourceCategories(get()) }
|
||||||
addFactory { SetMangaFilteredScanlators(get()) }
|
|
||||||
addFactory { GetAllManga(get()) }
|
addFactory { GetAllManga(get()) }
|
||||||
addFactory { GetMangaBySource(get()) }
|
addFactory { GetMangaBySource(get()) }
|
||||||
addFactory { DeleteChapters(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.copyFromSChapter
|
||||||
import eu.kanade.domain.chapter.model.toSChapter
|
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.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.toSManga
|
import eu.kanade.domain.manga.model.toSManga
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
@ -34,6 +35,7 @@ class SyncChaptersWithSource(
|
|||||||
private val updateManga: UpdateManga,
|
private val updateManga: UpdateManga,
|
||||||
private val updateChapter: UpdateChapter,
|
private val updateChapter: UpdateChapter,
|
||||||
private val getChaptersByMangaId: GetChaptersByMangaId,
|
private val getChaptersByMangaId: GetChaptersByMangaId,
|
||||||
|
private val getExcludedScanlators: GetExcludedScanlators,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -234,6 +236,10 @@ class SyncChaptersWithSource(
|
|||||||
|
|
||||||
val reAddedUrls = reAdded.map { it.url }.toHashSet()
|
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.domain.manga.model.downloadedFilter
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
import eu.kanade.tachiyomi.ui.manga.ChapterList
|
import eu.kanade.tachiyomi.ui.manga.ChapterList
|
||||||
import exh.md.utils.MdUtil
|
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.chapter.service.getChapterSort
|
import tachiyomi.domain.chapter.service.getChapterSort
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
@ -16,7 +15,7 @@ import tachiyomi.source.local.isLocal
|
|||||||
*/
|
*/
|
||||||
fun List<Chapter>.applyFilters(
|
fun List<Chapter>.applyFilters(
|
||||||
manga: Manga,
|
manga: Manga,
|
||||||
downloadManager: DownloadManager/* SY --> */,
|
downloadManager: DownloadManager,/* SY --> */
|
||||||
mergedManga: Map<Long, Manga>, /* SY <-- */
|
mergedManga: Map<Long, Manga>, /* SY <-- */
|
||||||
): List<Chapter> {
|
): List<Chapter> {
|
||||||
val isLocalManga = manga.isLocal()
|
val isLocalManga = manga.isLocal()
|
||||||
@ -41,14 +40,6 @@ fun List<Chapter>.applyFilters(
|
|||||||
downloaded || isLocalManga
|
downloaded || isLocalManga
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SY -->
|
|
||||||
.filter { chapter ->
|
|
||||||
manga.filteredScanlators.isNullOrEmpty() ||
|
|
||||||
MdUtil.getScanlators(chapter.scanlator).any { group ->
|
|
||||||
manga.filteredScanlators!!.contains(group)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// SY <--
|
|
||||||
.sortedWith(getChapterSort(manga))
|
.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(unreadFilter) { !chapter.read } }
|
||||||
.filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
|
.filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
|
||||||
.filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } }
|
.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) }
|
.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 {
|
fun Manga.chaptersFiltered(): Boolean {
|
||||||
return unreadFilter != TriState.DISABLED ||
|
return unreadFilter != TriState.DISABLED ||
|
||||||
downloadedFilter != TriState.DISABLED ||
|
downloadedFilter != TriState.DISABLED ||
|
||||||
bookmarkedFilter != TriState.DISABLED ||
|
bookmarkedFilter != TriState.DISABLED
|
||||||
// SY -->
|
|
||||||
!filteredScanlators.isNullOrEmpty()
|
|
||||||
// SY <--
|
|
||||||
}
|
}
|
||||||
fun Manga.forceDownloaded(): Boolean {
|
fun Manga.forceDownloaded(): Boolean {
|
||||||
return favorite && Injekt.get<BasePreferences>().downloadedOnly().get()
|
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.AlertDialog
|
||||||
import androidx.compose.material3.DropdownMenuItem
|
import androidx.compose.material3.DropdownMenuItem
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
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.RadioItem
|
||||||
import tachiyomi.presentation.core.components.SortItem
|
import tachiyomi.presentation.core.components.SortItem
|
||||||
import tachiyomi.presentation.core.components.TriStateItem
|
import tachiyomi.presentation.core.components.TriStateItem
|
||||||
|
import tachiyomi.presentation.core.theme.active
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ChapterSettingsDialog(
|
fun ChapterSettingsDialog(
|
||||||
@ -45,13 +47,12 @@ fun ChapterSettingsDialog(
|
|||||||
onDownloadFilterChanged: (TriState) -> Unit,
|
onDownloadFilterChanged: (TriState) -> Unit,
|
||||||
onUnreadFilterChanged: (TriState) -> Unit,
|
onUnreadFilterChanged: (TriState) -> Unit,
|
||||||
onBookmarkedFilterChanged: (TriState) -> Unit,
|
onBookmarkedFilterChanged: (TriState) -> Unit,
|
||||||
|
scanlatorFilterActive: Boolean,
|
||||||
|
onScanlatorFilterClicked: (() -> Unit),
|
||||||
onSortModeChanged: (Long) -> Unit,
|
onSortModeChanged: (Long) -> Unit,
|
||||||
onDisplayModeChanged: (Long) -> Unit,
|
onDisplayModeChanged: (Long) -> Unit,
|
||||||
onSetAsDefault: (applyToExistingManga: Boolean) -> Unit,
|
onSetAsDefault: (applyToExistingManga: Boolean) -> Unit,
|
||||||
onResetToDefault: () -> Unit,
|
onResetToDefault: () -> Unit,
|
||||||
// SY -->
|
|
||||||
onClickShowScanlatorSelection: (() -> Unit)?,
|
|
||||||
// SY <--
|
|
||||||
) {
|
) {
|
||||||
var showSetAsDefaultDialog by rememberSaveable { mutableStateOf(false) }
|
var showSetAsDefaultDialog by rememberSaveable { mutableStateOf(false) }
|
||||||
if (showSetAsDefaultDialog) {
|
if (showSetAsDefaultDialog) {
|
||||||
@ -94,14 +95,14 @@ fun ChapterSettingsDialog(
|
|||||||
0 -> {
|
0 -> {
|
||||||
FilterPage(
|
FilterPage(
|
||||||
downloadFilter = manga?.downloadedFilter ?: TriState.DISABLED,
|
downloadFilter = manga?.downloadedFilter ?: TriState.DISABLED,
|
||||||
onDownloadFilterChanged = onDownloadFilterChanged.takeUnless { manga?.forceDownloaded() == true },
|
onDownloadFilterChanged = onDownloadFilterChanged
|
||||||
|
.takeUnless { manga?.forceDownloaded() == true },
|
||||||
unreadFilter = manga?.unreadFilter ?: TriState.DISABLED,
|
unreadFilter = manga?.unreadFilter ?: TriState.DISABLED,
|
||||||
onUnreadFilterChanged = onUnreadFilterChanged,
|
onUnreadFilterChanged = onUnreadFilterChanged,
|
||||||
bookmarkedFilter = manga?.bookmarkedFilter ?: TriState.DISABLED,
|
bookmarkedFilter = manga?.bookmarkedFilter ?: TriState.DISABLED,
|
||||||
onBookmarkedFilterChanged = onBookmarkedFilterChanged,
|
onBookmarkedFilterChanged = onBookmarkedFilterChanged,
|
||||||
// SY -->
|
scanlatorFilterActive = scanlatorFilterActive,
|
||||||
onClickShowScanlatorSelection = onClickShowScanlatorSelection,
|
onScanlatorFilterClicked = onScanlatorFilterClicked,
|
||||||
// SY <--
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
1 -> {
|
1 -> {
|
||||||
@ -130,9 +131,8 @@ private fun ColumnScope.FilterPage(
|
|||||||
onUnreadFilterChanged: (TriState) -> Unit,
|
onUnreadFilterChanged: (TriState) -> Unit,
|
||||||
bookmarkedFilter: TriState,
|
bookmarkedFilter: TriState,
|
||||||
onBookmarkedFilterChanged: (TriState) -> Unit,
|
onBookmarkedFilterChanged: (TriState) -> Unit,
|
||||||
// SY -->
|
scanlatorFilterActive: Boolean,
|
||||||
onClickShowScanlatorSelection: (() -> Unit)?,
|
onScanlatorFilterClicked: (() -> Unit),
|
||||||
// SY <--
|
|
||||||
) {
|
) {
|
||||||
TriStateItem(
|
TriStateItem(
|
||||||
label = stringResource(R.string.label_downloaded),
|
label = stringResource(R.string.label_downloaded),
|
||||||
@ -149,23 +149,20 @@ private fun ColumnScope.FilterPage(
|
|||||||
state = bookmarkedFilter,
|
state = bookmarkedFilter,
|
||||||
onClick = onBookmarkedFilterChanged,
|
onClick = onBookmarkedFilterChanged,
|
||||||
)
|
)
|
||||||
// SY -->
|
ScanlatorFilterItem(
|
||||||
if (onClickShowScanlatorSelection != null) {
|
active = scanlatorFilterActive,
|
||||||
SetScanlatorsItem(onClickShowScanlatorSelection)
|
onClick = onScanlatorFilterClicked,
|
||||||
}
|
)
|
||||||
// SY <--
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun SetScanlatorsItem(
|
fun ScanlatorFilterItem(
|
||||||
onClickShowScanlatorSelection: () -> Unit,
|
active: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable(
|
.clickable(onClick = onClick)
|
||||||
onClick = onClickShowScanlatorSelection,
|
|
||||||
)
|
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp),
|
.padding(horizontal = TabbedDialogPaddings.Horizontal, vertical = 12.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
@ -174,6 +171,11 @@ private fun SetScanlatorsItem(
|
|||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Outlined.PeopleAlt,
|
imageVector = Icons.Outlined.PeopleAlt,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
|
tint = if (active) {
|
||||||
|
MaterialTheme.colorScheme.active
|
||||||
|
} else {
|
||||||
|
LocalContentColor.current
|
||||||
|
},
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.scanlator),
|
text = stringResource(R.string.scanlator),
|
||||||
@ -181,7 +183,6 @@ private fun SetScanlatorsItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SY <--
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ColumnScope.SortPage(
|
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.fastAll
|
||||||
import androidx.compose.ui.util.fastAny
|
import androidx.compose.ui.util.fastAny
|
||||||
import androidx.compose.ui.util.fastMap
|
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.ChapterDownloadAction
|
||||||
import eu.kanade.presentation.manga.components.ChapterHeader
|
import eu.kanade.presentation.manga.components.ChapterHeader
|
||||||
import eu.kanade.presentation.manga.components.ExpandableMangaDescription
|
import eu.kanade.presentation.manga.components.ExpandableMangaDescription
|
||||||
@ -375,7 +374,7 @@ private fun MangaScreenSmallImpl(
|
|||||||
title = state.manga.title,
|
title = state.manga.title,
|
||||||
titleAlphaProvider = { animatedTitleAlpha },
|
titleAlphaProvider = { animatedTitleAlpha },
|
||||||
backgroundAlphaProvider = { animatedBgAlpha },
|
backgroundAlphaProvider = { animatedBgAlpha },
|
||||||
hasFilters = state.manga.chaptersFiltered(),
|
hasFilters = state.filterActive,
|
||||||
onBackClicked = internalOnBackPressed,
|
onBackClicked = internalOnBackPressed,
|
||||||
onClickFilter = onFilterClicked,
|
onClickFilter = onFilterClicked,
|
||||||
onClickShare = onShareClicked,
|
onClickShare = onShareClicked,
|
||||||
@ -705,7 +704,7 @@ fun MangaScreenLargeImpl(
|
|||||||
title = state.manga.title,
|
title = state.manga.title,
|
||||||
titleAlphaProvider = { if (isAnySelected) 1f else 0f },
|
titleAlphaProvider = { if (isAnySelected) 1f else 0f },
|
||||||
backgroundAlphaProvider = { 1f },
|
backgroundAlphaProvider = { 1f },
|
||||||
hasFilters = state.manga.chaptersFiltered(),
|
hasFilters = state.filterActive,
|
||||||
onBackClicked = internalOnBackPressed,
|
onBackClicked = internalOnBackPressed,
|
||||||
onClickFilter = onFilterButtonClicked,
|
onClickFilter = onFilterButtonClicked,
|
||||||
onClickShare = onShareClicked,
|
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.DateColumnAdapter
|
||||||
import tachiyomi.data.History
|
import tachiyomi.data.History
|
||||||
import tachiyomi.data.Mangas
|
import tachiyomi.data.Mangas
|
||||||
import tachiyomi.data.StringListAndColumnAdapter
|
|
||||||
import tachiyomi.data.StringListColumnAdapter
|
import tachiyomi.data.StringListColumnAdapter
|
||||||
import tachiyomi.data.UpdateStrategyColumnAdapter
|
import tachiyomi.data.UpdateStrategyColumnAdapter
|
||||||
import tachiyomi.domain.UnsortedPreferences
|
import tachiyomi.domain.UnsortedPreferences
|
||||||
@ -126,9 +125,6 @@ class AppModule(val app: Application) : InjektModule {
|
|||||||
mangasAdapter = Mangas.Adapter(
|
mangasAdapter = Mangas.Adapter(
|
||||||
genreAdapter = StringListColumnAdapter,
|
genreAdapter = StringListColumnAdapter,
|
||||||
update_strategyAdapter = UpdateStrategyColumnAdapter,
|
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.BackupConst.BACKUP_TRACK_MASK
|
||||||
import eu.kanade.tachiyomi.data.backup.models.Backup
|
import eu.kanade.tachiyomi.data.backup.models.Backup
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
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.BackupFlatMetadata
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
import eu.kanade.tachiyomi.data.backup.models.BackupHistory
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
import eu.kanade.tachiyomi.data.backup.models.BackupManga
|
||||||
@ -252,10 +253,15 @@ class BackupCreator(
|
|||||||
// Check if user wants chapter information in backup
|
// Check if user wants chapter information in backup
|
||||||
if (options and BACKUP_CHAPTER_MASK == BACKUP_CHAPTER) {
|
if (options and BACKUP_CHAPTER_MASK == BACKUP_CHAPTER) {
|
||||||
// Backup all the chapters
|
// Backup all the chapters
|
||||||
val chapters = handler.awaitList { chaptersQueries.getChaptersByMangaId(manga.id, backupChapterMapper) }
|
handler.awaitList {
|
||||||
if (chapters.isNotEmpty()) {
|
chaptersQueries.getChaptersByMangaId(
|
||||||
mangaObject.chapters = chapters
|
mangaId = manga.id,
|
||||||
|
applyScanlatorFilter = 0, // false
|
||||||
|
mapper = backupChapterMapper,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
.takeUnless(List<BackupChapter>::isEmpty)
|
||||||
|
?.let { mangaObject.chapters = it }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if user wants category information in backup
|
// Check if user wants category information in backup
|
||||||
|
@ -27,7 +27,6 @@ import eu.kanade.tachiyomi.util.system.createFileInCacheDir
|
|||||||
import exh.EXHMigrations
|
import exh.EXHMigrations
|
||||||
import exh.source.MERGED_SOURCE_ID
|
import exh.source.MERGED_SOURCE_ID
|
||||||
import exh.util.nullIfBlank
|
import exh.util.nullIfBlank
|
||||||
import exh.util.nullIfEmpty
|
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
import tachiyomi.core.preference.AndroidPreferenceStore
|
import tachiyomi.core.preference.AndroidPreferenceStore
|
||||||
@ -35,7 +34,6 @@ import tachiyomi.core.preference.PreferenceStore
|
|||||||
import tachiyomi.data.DatabaseHandler
|
import tachiyomi.data.DatabaseHandler
|
||||||
import tachiyomi.data.Manga_sync
|
import tachiyomi.data.Manga_sync
|
||||||
import tachiyomi.data.Mangas
|
import tachiyomi.data.Mangas
|
||||||
import tachiyomi.data.StringListAndColumnAdapter
|
|
||||||
import tachiyomi.data.UpdateStrategyColumnAdapter
|
import tachiyomi.data.UpdateStrategyColumnAdapter
|
||||||
import tachiyomi.data.manga.MangaMapper
|
import tachiyomi.data.manga.MangaMapper
|
||||||
import tachiyomi.data.manga.MergedMangaMapper
|
import tachiyomi.data.manga.MergedMangaMapper
|
||||||
@ -365,9 +363,6 @@ class BackupRestorer(
|
|||||||
coverLastModified = manga.coverLastModified,
|
coverLastModified = manga.coverLastModified,
|
||||||
dateAdded = manga.dateAdded,
|
dateAdded = manga.dateAdded,
|
||||||
mangaId = manga.id,
|
mangaId = manga.id,
|
||||||
// SY -->
|
|
||||||
filteredScanlators = manga.filteredScanlators?.let(StringListAndColumnAdapter::encode),
|
|
||||||
// SY <--
|
|
||||||
updateStrategy = manga.updateStrategy.let(UpdateStrategyColumnAdapter::encode),
|
updateStrategy = manga.updateStrategy.let(UpdateStrategyColumnAdapter::encode),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -530,9 +525,6 @@ class BackupRestorer(
|
|||||||
chapterFlags = manga.chapterFlags,
|
chapterFlags = manga.chapterFlags,
|
||||||
coverLastModified = manga.coverLastModified,
|
coverLastModified = manga.coverLastModified,
|
||||||
dateAdded = manga.dateAdded,
|
dateAdded = manga.dateAdded,
|
||||||
// SY -->
|
|
||||||
filteredScanlators = manga.filteredScanlators?.nullIfEmpty(),
|
|
||||||
// SY <--
|
|
||||||
updateStrategy = manga.updateStrategy,
|
updateStrategy = manga.updateStrategy,
|
||||||
)
|
)
|
||||||
mangasQueries.selectLastInsertedRowId()
|
mangasQueries.selectLastInsertedRowId()
|
||||||
|
@ -4,7 +4,6 @@ import eu.kanade.tachiyomi.source.model.UpdateStrategy
|
|||||||
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
import eu.kanade.tachiyomi.ui.reader.setting.ReadingMode
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import kotlinx.serialization.protobuf.ProtoNumber
|
import kotlinx.serialization.protobuf.ProtoNumber
|
||||||
import tachiyomi.data.StringListAndColumnAdapter
|
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.manga.model.CustomMangaInfo
|
import tachiyomi.domain.manga.model.CustomMangaInfo
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
@ -79,7 +78,6 @@ data class BackupManga(
|
|||||||
chapterFlags = this@BackupManga.chapterFlags.toLong(),
|
chapterFlags = this@BackupManga.chapterFlags.toLong(),
|
||||||
updateStrategy = this@BackupManga.updateStrategy,
|
updateStrategy = this@BackupManga.updateStrategy,
|
||||||
lastModifiedAt = this@BackupManga.lastModifiedAt,
|
lastModifiedAt = this@BackupManga.lastModifiedAt,
|
||||||
filteredScanlators = this@BackupManga.filtered_scanlators?.let(StringListAndColumnAdapter::decode),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,7 +139,6 @@ data class BackupManga(
|
|||||||
lastModifiedAt = manga.lastModifiedAt,
|
lastModifiedAt = manga.lastModifiedAt,
|
||||||
favoriteModifiedAt = manga.favoriteModifiedAt,
|
favoriteModifiedAt = manga.favoriteModifiedAt,
|
||||||
// SY -->
|
// SY -->
|
||||||
filtered_scanlators = manga.filteredScanlators?.let(StringListAndColumnAdapter::encode),
|
|
||||||
).also { backupManga ->
|
).also { backupManga ->
|
||||||
customMangaInfo?.let {
|
customMangaInfo?.let {
|
||||||
backupManga.customTitle = it.title
|
backupManga.customTitle = it.title
|
||||||
|
@ -602,9 +602,9 @@ class LibraryScreenModel(
|
|||||||
// SY -->
|
// SY -->
|
||||||
val mergedManga = getMergedMangaById.await(manga.id).associateBy { it.id }
|
val mergedManga = getMergedMangaById.await(manga.id).associateBy { it.id }
|
||||||
return if (manga.id == MERGED_SOURCE_ID) {
|
return if (manga.id == MERGED_SOURCE_ID) {
|
||||||
getMergedChaptersByMangaId.await(manga.id)
|
getMergedChaptersByMangaId.await(manga.id, applyScanlatorFilter = true)
|
||||||
} else {
|
} else {
|
||||||
getChaptersByMangaId.await(manga.id)
|
getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true)
|
||||||
}.getNextUnread(manga, downloadManager, mergedManga)
|
}.getNextUnread(manga, downloadManager, mergedManga)
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
@ -33,7 +33,7 @@ import eu.kanade.presentation.manga.EditCoverAction
|
|||||||
import eu.kanade.presentation.manga.MangaScreen
|
import eu.kanade.presentation.manga.MangaScreen
|
||||||
import eu.kanade.presentation.manga.components.DeleteChaptersDialog
|
import eu.kanade.presentation.manga.components.DeleteChaptersDialog
|
||||||
import eu.kanade.presentation.manga.components.MangaCoverDialog
|
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.manga.components.SetIntervalDialog
|
||||||
import eu.kanade.presentation.util.AssistContentScreen
|
import eu.kanade.presentation.util.AssistContentScreen
|
||||||
import eu.kanade.presentation.util.Screen
|
import eu.kanade.presentation.util.Screen
|
||||||
@ -156,9 +156,17 @@ class MangaScreen(
|
|||||||
// SY -->
|
// SY -->
|
||||||
onWebViewClicked = {
|
onWebViewClicked = {
|
||||||
if (successState.mergedData == null) {
|
if (successState.mergedData == null) {
|
||||||
openMangaInWebView(navigator, screenModel.manga, screenModel.source)
|
openMangaInWebView(
|
||||||
|
navigator,
|
||||||
|
screenModel.manga,
|
||||||
|
screenModel.source
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
openMergedMangaWebview(context, navigator, successState.mergedData)
|
openMergedMangaWebview(
|
||||||
|
context,
|
||||||
|
navigator,
|
||||||
|
successState.mergedData
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}.takeIf { isHttpSource },
|
}.takeIf { isHttpSource },
|
||||||
// SY <--
|
// SY <--
|
||||||
@ -199,9 +207,7 @@ class MangaScreen(
|
|||||||
onInvertSelection = screenModel::invertSelection,
|
onInvertSelection = screenModel::invertSelection,
|
||||||
)
|
)
|
||||||
|
|
||||||
// SY -->
|
|
||||||
var showScanlatorsDialog by remember { mutableStateOf(false) }
|
var showScanlatorsDialog by remember { mutableStateOf(false) }
|
||||||
// SY <--
|
|
||||||
|
|
||||||
val onDismissRequest = { screenModel.dismissDialog() }
|
val onDismissRequest = { screenModel.dismissDialog() }
|
||||||
when (val dialog = successState.dialog) {
|
when (val dialog = successState.dialog) {
|
||||||
@ -240,9 +246,8 @@ class MangaScreen(
|
|||||||
onDisplayModeChanged = screenModel::setDisplayMode,
|
onDisplayModeChanged = screenModel::setDisplayMode,
|
||||||
onSetAsDefault = screenModel::setCurrentSettingsAsDefault,
|
onSetAsDefault = screenModel::setCurrentSettingsAsDefault,
|
||||||
onResetToDefault = screenModel::resetToDefaultSettings,
|
onResetToDefault = screenModel::resetToDefaultSettings,
|
||||||
// SY -->
|
scanlatorFilterActive = successState.scanlatorFilterActive,
|
||||||
onClickShowScanlatorSelection = { showScanlatorsDialog = true }.takeIf { successState.scanlators.size > 1 },
|
onScanlatorFilterClicked = { showScanlatorsDialog = true },
|
||||||
// SY <--
|
|
||||||
)
|
)
|
||||||
MangaScreenModel.Dialog.TrackSheet -> {
|
MangaScreenModel.Dialog.TrackSheet -> {
|
||||||
NavigatorAdaptiveSheet(
|
NavigatorAdaptiveSheet(
|
||||||
@ -306,16 +311,15 @@ class MangaScreen(
|
|||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
// SY -->
|
|
||||||
if (showScanlatorsDialog) {
|
if (showScanlatorsDialog) {
|
||||||
SelectScanlatorsDialog(
|
ScanlatorFilterDialog(
|
||||||
|
availableScanlators = successState.availableScanlators,
|
||||||
|
excludedScanlators = successState.excludedScanlators,
|
||||||
onDismissRequest = { showScanlatorsDialog = false },
|
onDismissRequest = { showScanlatorsDialog = false },
|
||||||
availableScanlators = successState.scanlators,
|
onConfirm = screenModel::setExcludedScanlators,
|
||||||
initialSelectedScanlators = successState.manga.filteredScanlators ?: successState.scanlators,
|
|
||||||
onSelectScanlators = screenModel::setScanlatorFilter,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// SY <--
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun continueReading(context: Context, unreadChapter: Chapter?) {
|
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.preference.asState
|
||||||
import eu.kanade.core.util.addOrRemove
|
import eu.kanade.core.util.addOrRemove
|
||||||
import eu.kanade.core.util.insertSeparators
|
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.SetReadStatus
|
||||||
import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
|
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.GetPagePreviews
|
||||||
import eu.kanade.domain.manga.interactor.UpdateManga
|
import eu.kanade.domain.manga.interactor.UpdateManga
|
||||||
import eu.kanade.domain.manga.model.PagePreview
|
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.copyFrom
|
||||||
import eu.kanade.domain.manga.model.downloadedFilter
|
import eu.kanade.domain.manga.model.downloadedFilter
|
||||||
import eu.kanade.domain.manga.model.toSManga
|
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.NetworkToLocalManga
|
||||||
import tachiyomi.domain.manga.interactor.SetCustomMangaInfo
|
import tachiyomi.domain.manga.interactor.SetCustomMangaInfo
|
||||||
import tachiyomi.domain.manga.interactor.SetMangaChapterFlags
|
import tachiyomi.domain.manga.interactor.SetMangaChapterFlags
|
||||||
import tachiyomi.domain.manga.interactor.SetMangaFilteredScanlators
|
|
||||||
import tachiyomi.domain.manga.interactor.UpdateMergedSettings
|
import tachiyomi.domain.manga.interactor.UpdateMergedSettings
|
||||||
import tachiyomi.domain.manga.model.CustomMangaInfo
|
import tachiyomi.domain.manga.model.CustomMangaInfo
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
@ -141,7 +144,6 @@ class MangaScreenModel(
|
|||||||
// SY -->
|
// SY -->
|
||||||
private val sourceManager: SourceManager = Injekt.get(),
|
private val sourceManager: SourceManager = Injekt.get(),
|
||||||
private val getManga: GetManga = Injekt.get(),
|
private val getManga: GetManga = Injekt.get(),
|
||||||
private val setMangaFilteredScanlators: SetMangaFilteredScanlators = Injekt.get(),
|
|
||||||
private val getMergedChaptersByMangaId: GetMergedChaptersByMangaId = Injekt.get(),
|
private val getMergedChaptersByMangaId: GetMergedChaptersByMangaId = Injekt.get(),
|
||||||
private val getMergedMangaById: GetMergedMangaById = Injekt.get(),
|
private val getMergedMangaById: GetMergedMangaById = Injekt.get(),
|
||||||
private val getMergedReferencesById: GetMergedReferencesById = Injekt.get(),
|
private val getMergedReferencesById: GetMergedReferencesById = Injekt.get(),
|
||||||
@ -157,6 +159,9 @@ class MangaScreenModel(
|
|||||||
private val setCustomMangaInfo: SetCustomMangaInfo = Injekt.get(),
|
private val setCustomMangaInfo: SetCustomMangaInfo = Injekt.get(),
|
||||||
// SY <--
|
// SY <--
|
||||||
private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(),
|
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 setMangaChapterFlags: SetMangaChapterFlags = Injekt.get(),
|
||||||
private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(),
|
private val setMangaDefaultChapterFlags: SetMangaDefaultChapterFlags = Injekt.get(),
|
||||||
private val setReadStatus: SetReadStatus = Injekt.get(),
|
private val setReadStatus: SetReadStatus = Injekt.get(),
|
||||||
@ -238,11 +243,11 @@ class MangaScreenModel(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
screenModelScope.launchIO {
|
screenModelScope.launchIO {
|
||||||
getMangaAndChapters.subscribe(mangaId)
|
getMangaAndChapters.subscribe(mangaId, applyScanlatorFilter = true)
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
// SY -->
|
// SY -->
|
||||||
.combine(
|
.combine(
|
||||||
getMergedChaptersByMangaId.subscribe(mangaId, true)
|
getMergedChaptersByMangaId.subscribe(mangaId, true, applyScanlatorFilter = true)
|
||||||
.distinctUntilChanged(),
|
.distinctUntilChanged(),
|
||||||
) { (manga, chapters), mergedChapters ->
|
) { (manga, chapters), mergedChapters ->
|
||||||
if (manga.source == MERGED_SOURCE_ID) {
|
if (manga.source == MERGED_SOURCE_ID) {
|
||||||
@ -311,19 +316,38 @@ class MangaScreenModel(
|
|||||||
// SY -->
|
// SY -->
|
||||||
meta = raiseMetadata(flatMetadata, it.source),
|
meta = raiseMetadata(flatMetadata, it.source),
|
||||||
mergedData = mergedData,
|
mergedData = mergedData,
|
||||||
scanlators = getChapterScanlators(manga, chapters),
|
|
||||||
// SY <--
|
// 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()
|
observeDownloads()
|
||||||
|
|
||||||
screenModelScope.launchIO {
|
screenModelScope.launchIO {
|
||||||
val manga = getMangaAndChapters.awaitManga(mangaId)
|
val manga = getMangaAndChapters.awaitManga(mangaId)
|
||||||
// SY -->
|
// 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)
|
.toChapterListItems(manga, null)
|
||||||
val mergedData = getMergedReferencesById.await(mangaId).takeIf { it.isNotEmpty() }?.let { references ->
|
val mergedData = getMergedReferencesById.await(mangaId).takeIf { it.isNotEmpty() }?.let { references ->
|
||||||
MergedMangaData(
|
MergedMangaData(
|
||||||
@ -351,6 +375,8 @@ class MangaScreenModel(
|
|||||||
source = source,
|
source = source,
|
||||||
isFromSource = isFromSource,
|
isFromSource = isFromSource,
|
||||||
chapters = chapters,
|
chapters = chapters,
|
||||||
|
availableScanlators = getAvailableScanlators.await(mangaId),
|
||||||
|
excludedScanlators = getExcludedScanlators.await(mangaId),
|
||||||
isRefreshingData = needRefreshInfo || needRefreshChapter,
|
isRefreshingData = needRefreshInfo || needRefreshChapter,
|
||||||
dialog = null,
|
dialog = null,
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -365,7 +391,6 @@ class MangaScreenModel(
|
|||||||
} else {
|
} else {
|
||||||
PagePreviewState.Unused
|
PagePreviewState.Unused
|
||||||
},
|
},
|
||||||
scanlators = getChapterScanlators(manga, chapters.map { it.chapter }),
|
|
||||||
alwaysShowReadingProgress = readerPreferences.preserveReadingPosition().get() && manga.isEhBasedManga(),
|
alwaysShowReadingProgress = readerPreferences.preserveReadingPosition().get() && manga.isEhBasedManga(),
|
||||||
// SY <--
|
// SY <--
|
||||||
)
|
)
|
||||||
@ -424,15 +449,6 @@ class MangaScreenModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// SY -->
|
// 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? {
|
private fun raiseMetadata(flatMetadata: FlatMetadata?, source: Source): RaisedSearchMetadata? {
|
||||||
return if (flatMetadata != null) {
|
return if (flatMetadata != null) {
|
||||||
val metaClass = source.getMainSource<MetadataSource<*, *>>()?.metaClass
|
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.
|
* Sets the active display mode.
|
||||||
* @param mode the mode to set.
|
* @param mode the mode to set.
|
||||||
@ -1535,6 +1542,12 @@ class MangaScreenModel(
|
|||||||
updateSuccessState { it.copy(dialog = Dialog.FullCover) }
|
updateSuccessState { it.copy(dialog = Dialog.FullCover) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setExcludedScanlators(excludedScanlators: Set<String>) {
|
||||||
|
screenModelScope.launchIO {
|
||||||
|
setExcludedScanlators.await(mangaId, excludedScanlators)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
fun showEditMangaInfoDialog() {
|
fun showEditMangaInfoDialog() {
|
||||||
mutableState.update { state ->
|
mutableState.update { state ->
|
||||||
@ -1570,6 +1583,8 @@ class MangaScreenModel(
|
|||||||
val source: Source,
|
val source: Source,
|
||||||
val isFromSource: Boolean,
|
val isFromSource: Boolean,
|
||||||
val chapters: List<ChapterList.Item>,
|
val chapters: List<ChapterList.Item>,
|
||||||
|
val availableScanlators: Set<String>,
|
||||||
|
val excludedScanlators: Set<String>,
|
||||||
val trackItems: List<TrackItem> = emptyList(),
|
val trackItems: List<TrackItem> = emptyList(),
|
||||||
val isRefreshingData: Boolean = false,
|
val isRefreshingData: Boolean = false,
|
||||||
val dialog: MangaScreenModel.Dialog? = null,
|
val dialog: MangaScreenModel.Dialog? = null,
|
||||||
@ -1581,11 +1596,9 @@ class MangaScreenModel(
|
|||||||
val showMergeInOverflow: Boolean,
|
val showMergeInOverflow: Boolean,
|
||||||
val showMergeWithAnother: Boolean,
|
val showMergeWithAnother: Boolean,
|
||||||
val pagePreviewsState: PagePreviewState,
|
val pagePreviewsState: PagePreviewState,
|
||||||
val scanlators: List<String>,
|
|
||||||
val alwaysShowReadingProgress: Boolean,
|
val alwaysShowReadingProgress: Boolean,
|
||||||
// SY <--
|
// SY <--
|
||||||
) : State {
|
) : State {
|
||||||
|
|
||||||
val processedChapters by lazy {
|
val processedChapters by lazy {
|
||||||
chapters.applyFilters(manga).toList()
|
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
|
val trackingAvailable: Boolean
|
||||||
get() = trackItems.isNotEmpty()
|
get() = trackItems.isNotEmpty()
|
||||||
|
|
||||||
@ -1638,11 +1657,6 @@ class MangaScreenModel(
|
|||||||
.filter { (chapter) -> applyFilter(unreadFilter) { !chapter.read } }
|
.filter { (chapter) -> applyFilter(unreadFilter) { !chapter.read } }
|
||||||
.filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
|
.filter { (chapter) -> applyFilter(bookmarkedFilter) { chapter.bookmark } }
|
||||||
.filter { applyFilter(downloadedFilter) { it.isDownloaded || isLocalManga } }
|
.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) }
|
.sortedWith { (chapter1), (chapter2) -> getChapterSort(manga).invoke(chapter1, chapter2) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -181,10 +181,10 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
// SY -->
|
// SY -->
|
||||||
val (chapters, mangaMap) = runBlocking {
|
val (chapters, mangaMap) = runBlocking {
|
||||||
if (manga.source == MERGED_SOURCE_ID) {
|
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 }
|
.associateBy { it.id }
|
||||||
} else {
|
} else {
|
||||||
getChaptersByMangaId.await(manga.id) to null
|
getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) to null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun isChapterDownloaded(chapter: Chapter): Boolean {
|
fun isChapterDownloaded(chapter: Chapter): Boolean {
|
||||||
@ -220,13 +220,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
|||||||
) ||
|
) ||
|
||||||
// SY <--
|
// SY <--
|
||||||
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ||
|
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_BOOKMARKED && !it.bookmark) ||
|
||||||
(manga.bookmarkedFilterRaw == Manga.CHAPTER_SHOW_NOT_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 <--
|
|
||||||
}
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
|
@ -339,9 +339,5 @@ object DebugFunctions {
|
|||||||
runBlocking { handler.await { ehQueries.migrateAllNhentaiToOtherLang(NHentai.otherId, sources) } }
|
runBlocking { handler.await { ehQueries.migrateAllNhentaiToOtherLang(NHentai.otherId, sources) } }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resetFilteredScanlatorsForAllManga() {
|
|
||||||
runBlocking { handler.await { ehQueries.resetFilteredScanlatorsForAllManga() } }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun exportProtobufScheme() = ProtoBufSchemaGenerator.generateSchemaText(Backup.serializer().descriptor)
|
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()
|
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)!!,
|
chapter_flags = cursor.getLong(15)!!,
|
||||||
cover_last_modified = cursor.getLong(16)!!,
|
cover_last_modified = cursor.getLong(16)!!,
|
||||||
date_added = cursor.getLong(17)!!,
|
date_added = cursor.getLong(17)!!,
|
||||||
filtered_scanlators = cursor.getString(18)?.let(StringListAndColumnAdapter::decode),
|
filtered_scanlators = null,
|
||||||
update_strategy = UpdateStrategyColumnAdapter.decode(cursor.getLong(19)!!),
|
update_strategy = UpdateStrategyColumnAdapter.decode(cursor.getLong(19)!!),
|
||||||
calculate_interval = cursor.getLong(20)!!,
|
calculate_interval = cursor.getLong(20)!!,
|
||||||
last_modified_at = cursor.getLong(21)!!,
|
last_modified_at = cursor.getLong(21)!!,
|
||||||
@ -71,8 +71,12 @@ class LibraryQuery(
|
|||||||
coalesce(max(chapters.date_fetch), 0) AS fetchedAt,
|
coalesce(max(chapters.date_fetch), 0) AS fetchedAt,
|
||||||
sum(chapters.bookmark) AS bookmarkCount
|
sum(chapters.bookmark) AS bookmarkCount
|
||||||
FROM chapters
|
FROM chapters
|
||||||
|
LEFT JOIN excluded_scanlators
|
||||||
|
ON chapters.manga_id = excluded_scanlators.manga_id
|
||||||
|
AND chapters.scanlator = excluded_scanlators.scanlator
|
||||||
LEFT JOIN history
|
LEFT JOIN history
|
||||||
ON chapters._id = history.chapter_id
|
ON chapters._id = history.chapter_id
|
||||||
|
WHERE excluded_scanlators.scanlator IS NULL
|
||||||
GROUP BY chapters.manga_id
|
GROUP BY chapters.manga_id
|
||||||
) AS C
|
) AS C
|
||||||
ON M._id = C.manga_id
|
ON M._id = C.manga_id
|
||||||
@ -106,10 +110,14 @@ class LibraryQuery(
|
|||||||
coalesce(max(chapters.date_fetch), 0) AS fetchedAt,
|
coalesce(max(chapters.date_fetch), 0) AS fetchedAt,
|
||||||
sum(chapters.bookmark) AS bookmarkCount
|
sum(chapters.bookmark) AS bookmarkCount
|
||||||
FROM chapters
|
FROM chapters
|
||||||
|
LEFT JOIN excluded_scanlators
|
||||||
|
ON chapters.manga_id = excluded_scanlators.manga_id
|
||||||
|
AND chapters.scanlator = excluded_scanlators.scanlator
|
||||||
LEFT JOIN history
|
LEFT JOIN history
|
||||||
ON chapters._id = history.chapter_id
|
ON chapters._id = history.chapter_id
|
||||||
LEFT JOIN merged as ME
|
LEFT JOIN merged as ME
|
||||||
ON ME.manga_id = chapters.manga_id
|
ON ME.manga_id = chapters.manga_id
|
||||||
|
WHERE excluded_scanlators.scanlator IS NULL
|
||||||
GROUP BY ME.merge_id
|
GROUP BY ME.merge_id
|
||||||
) AS C
|
) AS C
|
||||||
ON ME.merge_id = C.merge_id
|
ON ME.merge_id = C.merge_id
|
||||||
|
@ -2,6 +2,7 @@ package tachiyomi.data.chapter
|
|||||||
|
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import logcat.LogPriority
|
import logcat.LogPriority
|
||||||
|
import tachiyomi.core.util.lang.toLong
|
||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import tachiyomi.data.DatabaseHandler
|
import tachiyomi.data.DatabaseHandler
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
@ -76,8 +77,22 @@ class ChapterRepositoryImpl(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getChapterByMangaId(mangaId: Long): List<Chapter> {
|
override suspend fun getChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean): List<Chapter> {
|
||||||
return handler.awaitList { chaptersQueries.getChaptersByMangaId(mangaId, ChapterMapper::mapChapter) }
|
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> {
|
override suspend fun getBookmarkedChaptersByMangaId(mangaId: Long): List<Chapter> {
|
||||||
@ -93,12 +108,9 @@ class ChapterRepositoryImpl(
|
|||||||
return handler.awaitOneOrNull { chaptersQueries.getChapterById(id, ChapterMapper::mapChapter) }
|
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 {
|
return handler.subscribeToList {
|
||||||
chaptersQueries.getChaptersByMangaId(
|
chaptersQueries.getChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ChapterMapper::mapChapter)
|
||||||
mangaId,
|
|
||||||
ChapterMapper::mapChapter,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,13 +129,13 @@ class ChapterRepositoryImpl(
|
|||||||
return handler.awaitList { chaptersQueries.getChapterByUrl(url, ChapterMapper::mapChapter) }
|
return handler.awaitList { chaptersQueries.getChapterByUrl(url, ChapterMapper::mapChapter) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getMergedChapterByMangaId(mangaId: Long): List<Chapter> {
|
override suspend fun getMergedChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean): List<Chapter> {
|
||||||
return handler.awaitList { chaptersQueries.getMergedChaptersByMangaId(mangaId, ChapterMapper::mapChapter) }
|
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 {
|
return handler.subscribeToList {
|
||||||
chaptersQueries.getMergedChaptersByMangaId(mangaId, ChapterMapper::mapChapter)
|
chaptersQueries.getMergedChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ChapterMapper::mapChapter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
@ -25,7 +25,10 @@ object MangaMapper {
|
|||||||
chapterFlags: Long,
|
chapterFlags: Long,
|
||||||
coverLastModified: Long,
|
coverLastModified: Long,
|
||||||
dateAdded: Long,
|
dateAdded: Long,
|
||||||
filteredScanlators: List<String>?,
|
// SY -->
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
filteredScanlators: String?,
|
||||||
|
// SY <--
|
||||||
updateStrategy: UpdateStrategy,
|
updateStrategy: UpdateStrategy,
|
||||||
calculateInterval: Long,
|
calculateInterval: Long,
|
||||||
lastModifiedAt: Long,
|
lastModifiedAt: Long,
|
||||||
@ -53,9 +56,6 @@ object MangaMapper {
|
|||||||
thumbnailUrl = thumbnailUrl,
|
thumbnailUrl = thumbnailUrl,
|
||||||
updateStrategy = updateStrategy,
|
updateStrategy = updateStrategy,
|
||||||
initialized = initialized,
|
initialized = initialized,
|
||||||
// SY -->
|
|
||||||
filteredScanlators = filteredScanlators,
|
|
||||||
// SY <--
|
|
||||||
lastModifiedAt = lastModifiedAt,
|
lastModifiedAt = lastModifiedAt,
|
||||||
favoriteModifiedAt = favoriteModifiedAt,
|
favoriteModifiedAt = favoriteModifiedAt,
|
||||||
)
|
)
|
||||||
@ -79,7 +79,10 @@ object MangaMapper {
|
|||||||
chapterFlags: Long,
|
chapterFlags: Long,
|
||||||
coverLastModified: Long,
|
coverLastModified: Long,
|
||||||
dateAdded: Long,
|
dateAdded: Long,
|
||||||
filteredScanlators: List<String>?,
|
// SY -->
|
||||||
|
@Suppress("UNUSED_PARAMETER")
|
||||||
|
filteredScanlators: String?,
|
||||||
|
// SY <--
|
||||||
updateStrategy: UpdateStrategy,
|
updateStrategy: UpdateStrategy,
|
||||||
calculateInterval: Long,
|
calculateInterval: Long,
|
||||||
lastModifiedAt: Long,
|
lastModifiedAt: Long,
|
||||||
@ -112,7 +115,7 @@ object MangaMapper {
|
|||||||
coverLastModified,
|
coverLastModified,
|
||||||
dateAdded,
|
dateAdded,
|
||||||
// SY -->
|
// SY -->
|
||||||
filteredScanlators,
|
null,
|
||||||
// SY <--
|
// SY <--
|
||||||
updateStrategy,
|
updateStrategy,
|
||||||
calculateInterval,
|
calculateInterval,
|
||||||
@ -150,7 +153,6 @@ object MangaMapper {
|
|||||||
thumbnailUrl = libraryView.thumbnail_url,
|
thumbnailUrl = libraryView.thumbnail_url,
|
||||||
updateStrategy = libraryView.update_strategy,
|
updateStrategy = libraryView.update_strategy,
|
||||||
initialized = libraryView.initialized,
|
initialized = libraryView.initialized,
|
||||||
filteredScanlators = libraryView.filtered_scanlators,
|
|
||||||
fetchInterval = libraryView.calculate_interval.toInt(),
|
fetchInterval = libraryView.calculate_interval.toInt(),
|
||||||
lastModifiedAt = libraryView.last_modified_at,
|
lastModifiedAt = libraryView.last_modified_at,
|
||||||
favoriteModifiedAt = libraryView.favorite_modified_at,
|
favoriteModifiedAt = libraryView.favorite_modified_at,
|
||||||
|
@ -6,7 +6,6 @@ import logcat.LogPriority
|
|||||||
import tachiyomi.core.util.system.logcat
|
import tachiyomi.core.util.system.logcat
|
||||||
import tachiyomi.data.AndroidDatabaseHandler
|
import tachiyomi.data.AndroidDatabaseHandler
|
||||||
import tachiyomi.data.DatabaseHandler
|
import tachiyomi.data.DatabaseHandler
|
||||||
import tachiyomi.data.StringListAndColumnAdapter
|
|
||||||
import tachiyomi.data.StringListColumnAdapter
|
import tachiyomi.data.StringListColumnAdapter
|
||||||
import tachiyomi.data.UpdateStrategyColumnAdapter
|
import tachiyomi.data.UpdateStrategyColumnAdapter
|
||||||
import tachiyomi.domain.library.model.LibraryManga
|
import tachiyomi.domain.library.model.LibraryManga
|
||||||
@ -119,9 +118,6 @@ class MangaRepositoryImpl(
|
|||||||
chapterFlags = manga.chapterFlags,
|
chapterFlags = manga.chapterFlags,
|
||||||
coverLastModified = manga.coverLastModified,
|
coverLastModified = manga.coverLastModified,
|
||||||
dateAdded = manga.dateAdded,
|
dateAdded = manga.dateAdded,
|
||||||
// SY -->
|
|
||||||
filteredScanlators = manga.filteredScanlators,
|
|
||||||
// SY <--
|
|
||||||
updateStrategy = manga.updateStrategy,
|
updateStrategy = manga.updateStrategy,
|
||||||
)
|
)
|
||||||
mangasQueries.selectLastInsertedRowId()
|
mangasQueries.selectLastInsertedRowId()
|
||||||
@ -170,9 +166,6 @@ class MangaRepositoryImpl(
|
|||||||
chapterFlags = value.chapterFlags,
|
chapterFlags = value.chapterFlags,
|
||||||
coverLastModified = value.coverLastModified,
|
coverLastModified = value.coverLastModified,
|
||||||
dateAdded = value.dateAdded,
|
dateAdded = value.dateAdded,
|
||||||
// SY -->
|
|
||||||
filteredScanlators = value.filteredScanlators?.let(StringListAndColumnAdapter::encode),
|
|
||||||
// SY <--
|
|
||||||
mangaId = value.id,
|
mangaId = value.id,
|
||||||
updateStrategy = value.updateStrategy?.let(UpdateStrategyColumnAdapter::encode),
|
updateStrategy = value.updateStrategy?.let(UpdateStrategyColumnAdapter::encode),
|
||||||
)
|
)
|
||||||
|
@ -36,7 +36,19 @@ FROM chapters
|
|||||||
WHERE _id = :id;
|
WHERE _id = :id;
|
||||||
|
|
||||||
getChaptersByMangaId:
|
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
|
FROM chapters
|
||||||
WHERE manga_id = :mangaId;
|
WHERE manga_id = :mangaId;
|
||||||
|
|
||||||
@ -58,12 +70,20 @@ WHERE url = :chapterUrl
|
|||||||
AND manga_id = :mangaId;
|
AND manga_id = :mangaId;
|
||||||
|
|
||||||
getMergedChaptersByMangaId:
|
getMergedChaptersByMangaId:
|
||||||
SELECT chapters.*
|
SELECT C.*
|
||||||
FROM (
|
FROM chapters C
|
||||||
SELECT manga_id FROM merged WHERE merge_id = ?
|
JOIN (
|
||||||
|
SELECT manga_id FROM merged WHERE merge_id = :mangaId
|
||||||
) AS M
|
) AS M
|
||||||
JOIN chapters
|
ON C.manga_id = M.manga_id
|
||||||
ON chapters.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:
|
removeChaptersWithIds:
|
||||||
DELETE FROM chapters
|
DELETE FROM chapters
|
||||||
|
@ -9,10 +9,6 @@ WHERE source = :oldId;
|
|||||||
getChaptersByMangaIds:
|
getChaptersByMangaIds:
|
||||||
SELECT * FROM chapters WHERE manga_id IN :mangaIds;
|
SELECT * FROM chapters WHERE manga_id IN :mangaIds;
|
||||||
|
|
||||||
resetFilteredScanlatorsForAllManga:
|
|
||||||
UPDATE mangas
|
|
||||||
SET filtered_scanlators = NULL;
|
|
||||||
|
|
||||||
migrateAllNhentaiToOtherLang:
|
migrateAllNhentaiToOtherLang:
|
||||||
UPDATE mangas
|
UPDATE mangas
|
||||||
SET source = :nh
|
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,
|
chapter_flags INTEGER NOT NULL,
|
||||||
cover_last_modified INTEGER NOT NULL,
|
cover_last_modified INTEGER NOT NULL,
|
||||||
date_added 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,
|
update_strategy INTEGER AS UpdateStrategy NOT NULL DEFAULT 0,
|
||||||
calculate_interval INTEGER DEFAULT 0 NOT NULL,
|
calculate_interval INTEGER DEFAULT 0 NOT NULL,
|
||||||
last_modified_at INTEGER NOT NULL DEFAULT 0,
|
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:
|
||||||
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)
|
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, :filteredScanlators, :updateStrategy, :calculateInterval, strftime('%s', 'now'));
|
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:
|
||||||
UPDATE mangas SET
|
UPDATE mangas SET
|
||||||
@ -153,7 +153,6 @@ UPDATE mangas SET
|
|||||||
chapter_flags = coalesce(:chapterFlags, chapter_flags),
|
chapter_flags = coalesce(:chapterFlags, chapter_flags),
|
||||||
cover_last_modified = coalesce(:coverLastModified, cover_last_modified),
|
cover_last_modified = coalesce(:coverLastModified, cover_last_modified),
|
||||||
date_added = coalesce(:dateAdded, date_added),
|
date_added = coalesce(:dateAdded, date_added),
|
||||||
filtered_scanlators = coalesce(:filteredScanlators, filtered_scanlators),
|
|
||||||
update_strategy = coalesce(:updateStrategy, update_strategy),
|
update_strategy = coalesce(:updateStrategy, update_strategy),
|
||||||
calculate_interval = coalesce(:calculateInterval, calculate_interval)
|
calculate_interval = coalesce(:calculateInterval, calculate_interval)
|
||||||
WHERE _id = :mangaId;
|
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,
|
coalesce(max(chapters.date_fetch), 0) AS fetchedAt,
|
||||||
sum(chapters.bookmark) AS bookmarkCount
|
sum(chapters.bookmark) AS bookmarkCount
|
||||||
FROM chapters
|
FROM chapters
|
||||||
|
LEFT JOIN excluded_scanlators
|
||||||
|
ON chapters.manga_id = excluded_scanlators.manga_id
|
||||||
|
AND chapters.scanlator = excluded_scanlators.scanlator
|
||||||
LEFT JOIN history
|
LEFT JOIN history
|
||||||
ON chapters._id = history.chapter_id
|
ON chapters._id = history.chapter_id
|
||||||
|
WHERE excluded_scanlators.scanlator IS NULL
|
||||||
GROUP BY chapters.manga_id
|
GROUP BY chapters.manga_id
|
||||||
) AS C
|
) AS C
|
||||||
ON M._id = C.manga_id
|
ON M._id = C.manga_id
|
||||||
|
@ -9,9 +9,9 @@ class GetChaptersByMangaId(
|
|||||||
private val chapterRepository: ChapterRepository,
|
private val chapterRepository: ChapterRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun await(mangaId: Long): List<Chapter> {
|
suspend fun await(mangaId: Long, applyScanlatorFilter: Boolean = false): List<Chapter> {
|
||||||
return try {
|
return try {
|
||||||
chapterRepository.getChapterByMangaId(mangaId)
|
chapterRepository.getChapterByMangaId(mangaId, applyScanlatorFilter)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
emptyList()
|
emptyList()
|
||||||
|
@ -16,13 +16,21 @@ class GetMergedChaptersByMangaId(
|
|||||||
private val getMergedReferencesById: GetMergedReferencesById,
|
private val getMergedReferencesById: GetMergedReferencesById,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun await(mangaId: Long, dedupe: Boolean = true): List<Chapter> {
|
suspend fun await(
|
||||||
return transformMergedChapters(getMergedReferencesById.await(mangaId), getFromDatabase(mangaId), dedupe)
|
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 {
|
return try {
|
||||||
chapterRepository.getMergedChapterByMangaIdAsFlow(mangaId)
|
chapterRepository.getMergedChapterByMangaIdAsFlow(mangaId, applyScanlatorFilter)
|
||||||
.combine(getMergedReferencesById.subscribe(mangaId)) { chapters, references ->
|
.combine(getMergedReferencesById.subscribe(mangaId)) { chapters, references ->
|
||||||
transformMergedChapters(references, chapters, dedupe)
|
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 {
|
return try {
|
||||||
chapterRepository.getMergedChapterByMangaId(mangaId)
|
chapterRepository.getMergedChapterByMangaId(mangaId, applyScanlatorFilter)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun transformMergedChapters(
|
private fun transformMergedChapters(
|
||||||
mangaReferences: List<MergedMangaReference>,
|
mangaReferences: List<MergedMangaReference>,
|
||||||
chapterList: List<Chapter>,
|
chapterList: List<Chapter>,
|
||||||
dedupe: Boolean,
|
dedupe: Boolean,
|
||||||
|
@ -14,21 +14,25 @@ interface ChapterRepository {
|
|||||||
|
|
||||||
suspend fun removeChaptersWithIds(chapterIds: List<Long>)
|
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 getBookmarkedChaptersByMangaId(mangaId: Long): List<Chapter>
|
||||||
|
|
||||||
suspend fun getChapterById(id: Long): 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?
|
suspend fun getChapterByUrlAndMangaId(url: String, mangaId: Long): Chapter?
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
suspend fun getChapterByUrl(url: String): List<Chapter>
|
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 <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ class GetNextChapters(
|
|||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
if (manga.source == MERGED_SOURCE_ID) {
|
if (manga.source == MERGED_SOURCE_ID) {
|
||||||
val chapters = getMergedChaptersByMangaId.await(mangaId)
|
val chapters = getMergedChaptersByMangaId.await(mangaId, applyScanlatorFilter = true)
|
||||||
.sortedWith(getChapterSort(manga, sortDescending = false))
|
.sortedWith(getChapterSort(manga, sortDescending = false))
|
||||||
|
|
||||||
return if (onlyUnread) {
|
return if (onlyUnread) {
|
||||||
@ -39,7 +39,7 @@ class GetNextChapters(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (manga.isEhBasedManga()) {
|
if (manga.isEhBasedManga()) {
|
||||||
val chapters = getChaptersByMangaId.await(mangaId)
|
val chapters = getChaptersByMangaId.await(mangaId, applyScanlatorFilter = true)
|
||||||
.sortedWith(getChapterSort(manga, sortDescending = false))
|
.sortedWith(getChapterSort(manga, sortDescending = false))
|
||||||
|
|
||||||
return if (onlyUnread) {
|
return if (onlyUnread) {
|
||||||
@ -50,7 +50,7 @@ class GetNextChapters(
|
|||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
val chapters = getChaptersByMangaId.await(mangaId)
|
val chapters = getChaptersByMangaId.await(mangaId, applyScanlatorFilter = true)
|
||||||
.sortedWith(getChapterSort(manga, sortDescending = false))
|
.sortedWith(getChapterSort(manga, sortDescending = false))
|
||||||
|
|
||||||
return if (onlyUnread) {
|
return if (onlyUnread) {
|
||||||
|
@ -24,7 +24,7 @@ class FetchInterval(
|
|||||||
} else {
|
} else {
|
||||||
window
|
window
|
||||||
}
|
}
|
||||||
val chapters = getChaptersByMangaId.await(manga.id)
|
val chapters = getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true)
|
||||||
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(
|
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(
|
||||||
chapters,
|
chapters,
|
||||||
dateTime.zone,
|
dateTime.zone,
|
||||||
|
@ -12,10 +12,10 @@ class GetMangaWithChapters(
|
|||||||
private val chapterRepository: ChapterRepository,
|
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(
|
return combine(
|
||||||
mangaRepository.getMangaByIdAsFlow(id),
|
mangaRepository.getMangaByIdAsFlow(id),
|
||||||
chapterRepository.getChapterByMangaIdAsFlow(id),
|
chapterRepository.getChapterByMangaIdAsFlow(id, applyScanlatorFilter),
|
||||||
) { manga, chapters ->
|
) { manga, chapters ->
|
||||||
Pair(manga, chapters)
|
Pair(manga, chapters)
|
||||||
}
|
}
|
||||||
@ -25,7 +25,7 @@ class GetMangaWithChapters(
|
|||||||
return mangaRepository.getMangaById(id)
|
return mangaRepository.getMangaById(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun awaitChapters(id: Long): List<Chapter> {
|
suspend fun awaitChapters(id: Long, applyScanlatorFilter: Boolean = false): List<Chapter> {
|
||||||
return chapterRepository.getChapterByMangaId(id)
|
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 initialized: Boolean,
|
||||||
val lastModifiedAt: Long,
|
val lastModifiedAt: Long,
|
||||||
val favoriteModifiedAt: Long?,
|
val favoriteModifiedAt: Long?,
|
||||||
// SY -->
|
|
||||||
val filteredScanlators: List<String>?,
|
|
||||||
// SY <--
|
|
||||||
) : Serializable {
|
) : Serializable {
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
@ -152,9 +149,6 @@ data class Manga(
|
|||||||
initialized = false,
|
initialized = false,
|
||||||
lastModifiedAt = 0L,
|
lastModifiedAt = 0L,
|
||||||
favoriteModifiedAt = null,
|
favoriteModifiedAt = null,
|
||||||
// SY -->
|
|
||||||
filteredScanlators = null,
|
|
||||||
// SY <--
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// SY -->
|
// SY -->
|
||||||
|
@ -52,8 +52,5 @@ fun Manga.toMangaUpdate(): MangaUpdate {
|
|||||||
thumbnailUrl = thumbnailUrl,
|
thumbnailUrl = thumbnailUrl,
|
||||||
updateStrategy = updateStrategy,
|
updateStrategy = updateStrategy,
|
||||||
initialized = initialized,
|
initialized = initialized,
|
||||||
// SY -->
|
|
||||||
filteredScanlators = filteredScanlators,
|
|
||||||
// SY <--
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -434,7 +434,6 @@
|
|||||||
<string name="short_title">Titre court</string>
|
<string name="short_title">Titre court</string>
|
||||||
<string name="cover_image_file_type">Type de fichier d\'image de couverture</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="thumbnail_image_file_type">Type de fichier d\'image miniature</string>
|
||||||
<string name="scanlator">Scanlator</string>
|
|
||||||
<string name="url">Url</string>
|
<string name="url">Url</string>
|
||||||
<string name="uploader_capital">Uploader en majuscule</string>
|
<string name="uploader_capital">Uploader en majuscule</string>
|
||||||
<string name="uploader">Uploader</string>
|
<string name="uploader">Uploader</string>
|
||||||
|
@ -545,7 +545,6 @@
|
|||||||
<string name="short_title">Short Title</string>
|
<string name="short_title">Short Title</string>
|
||||||
<string name="cover_image_file_type">Cover image file type</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="thumbnail_image_file_type">Thumbnail image file type</string>
|
||||||
<string name="scanlator">Scanlator</string>
|
|
||||||
<string name="url">Url</string>
|
<string name="url">Url</string>
|
||||||
<string name="uploader_capital">Uploader Capitalized</string>
|
<string name="uploader_capital">Uploader Capitalized</string>
|
||||||
<string name="uploader">Uploader</string>
|
<string name="uploader">Uploader</string>
|
||||||
|
@ -515,7 +515,6 @@
|
|||||||
<string name="short_title">Título curto</string>
|
<string name="short_title">Título curto</string>
|
||||||
<string name="cover_image_file_type">Tipo arq. da capa</string>
|
<string name="cover_image_file_type">Tipo arq. da capa</string>
|
||||||
<string name="thumbnail_image_file_type">Tipo arq. miniatura</string>
|
<string name="thumbnail_image_file_type">Tipo arq. miniatura</string>
|
||||||
<string name="scanlator">Scanlator</string>
|
|
||||||
<string name="url">Url</string>
|
<string name="url">Url</string>
|
||||||
<string name="uploader_capital">Uploader Capitalizado</string>
|
<string name="uploader_capital">Uploader Capitalizado</string>
|
||||||
<string name="uploader">Uploader</string>
|
<string name="uploader">Uploader</string>
|
||||||
|
@ -616,7 +616,6 @@
|
|||||||
<string name="short_title">Краткое название</string>
|
<string name="short_title">Краткое название</string>
|
||||||
<string name="cover_image_file_type">Тип изображения обложки</string>
|
<string name="cover_image_file_type">Тип изображения обложки</string>
|
||||||
<string name="thumbnail_image_file_type">Тип изображения миниатюры</string>
|
<string name="thumbnail_image_file_type">Тип изображения миниатюры</string>
|
||||||
<string name="scanlator">Переводчик</string>
|
|
||||||
<string name="url">URL-адрес</string>
|
<string name="url">URL-адрес</string>
|
||||||
<string name="uploader_capital">Имя загрузчика с заглавной буквы</string>
|
<string name="uploader_capital">Имя загрузчика с заглавной буквы</string>
|
||||||
<string name="uploader">Имя загрузчика</string>
|
<string name="uploader">Имя загрузчика</string>
|
||||||
|
@ -582,7 +582,6 @@
|
|||||||
<string name="short_title">短标题</string>
|
<string name="short_title">短标题</string>
|
||||||
<string name="cover_image_file_type">转换图像类型</string>
|
<string name="cover_image_file_type">转换图像类型</string>
|
||||||
<string name="thumbnail_image_file_type">缓存图像类型</string>
|
<string name="thumbnail_image_file_type">缓存图像类型</string>
|
||||||
<string name="scanlator">扫译组</string>
|
|
||||||
<string name="uploader_capital">上传者名字</string>
|
<string name="uploader_capital">上传者名字</string>
|
||||||
<string name="uploader">上传者</string>
|
<string name="uploader">上传者</string>
|
||||||
<string name="rating_string">评分字符串</string>
|
<string name="rating_string">评分字符串</string>
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
<string name="track">Tracking</string>
|
<string name="track">Tracking</string>
|
||||||
<string name="delete_downloaded">Delete downloaded</string>
|
<string name="delete_downloaded">Delete downloaded</string>
|
||||||
<string name="history">History</string>
|
<string name="history">History</string>
|
||||||
|
<string name="scanlator">Scanlator</string>
|
||||||
|
|
||||||
<!-- Screen titles -->
|
<!-- Screen titles -->
|
||||||
<string name="label_more">More</string>
|
<string name="label_more">More</string>
|
||||||
@ -702,6 +703,8 @@
|
|||||||
<string name="set_chapter_settings_as_default">Set as default</string>
|
<string name="set_chapter_settings_as_default">Set as default</string>
|
||||||
<string name="no_chapters_error">No chapters found</string>
|
<string name="no_chapters_error">No chapters found</string>
|
||||||
<string name="are_you_sure">Are you sure?</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 -->
|
<!-- Tracking Screen -->
|
||||||
<string name="manga_tracking_tab">Tracking</string>
|
<string name="manga_tracking_tab">Tracking</string>
|
||||||
|
@ -613,7 +613,6 @@
|
|||||||
<string name="short_title">Short Title</string>
|
<string name="short_title">Short Title</string>
|
||||||
<string name="cover_image_file_type">Cover image file type</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="thumbnail_image_file_type">Thumbnail image file type</string>
|
||||||
<string name="scanlator">Scanlator</string>
|
|
||||||
<string name="url">Url</string>
|
<string name="url">Url</string>
|
||||||
<string name="uploader_capital">Uploader Capitalized</string>
|
<string name="uploader_capital">Uploader Capitalized</string>
|
||||||
<string name="uploader">Uploader</string>
|
<string name="uploader">Uploader</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user