feat(history): Add indicator for un-finished reading & has more un-read chapters (#992)
* feat(history): Add indicator for un-finished reading & has more un-read chapters * Fix StateProvider * Show reading progress in History screen * Don't count bookmark excluded chapters in unread dot for History * Don't count bookmark excluded chapters in Library unread count * Exclude bookmark filtered chapters from continue-reading list * Bundle `applyFilter` for bookmark & scanlator together * Remove parentheses
This commit is contained in:
parent
3ec6aeaba2
commit
5dae177b08
26 changed files with 461 additions and 65 deletions
|
|
@ -25,8 +25,8 @@ android {
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "app.komikku"
|
applicationId = "app.komikku"
|
||||||
|
|
||||||
versionCode = 74
|
versionCode = 75
|
||||||
versionName = "1.13.2"
|
versionName = "1.13.3"
|
||||||
|
|
||||||
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")
|
||||||
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
buildConfigField("String", "COMMIT_SHA", "\"${getGitSha()}\"")
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,15 @@ private fun HistoryScreenContent(
|
||||||
onClickDelete = { onClickDelete(value) },
|
onClickDelete = { onClickDelete(value) },
|
||||||
onClickFavorite = { onClickFavorite(value) },
|
onClickFavorite = { onClickFavorite(value) },
|
||||||
// KMK -->
|
// KMK -->
|
||||||
|
readProgress = value.lastPageRead
|
||||||
|
.takeIf { !value.read && it > 0L }
|
||||||
|
?.let {
|
||||||
|
stringResource(
|
||||||
|
MR.strings.chapter_progress,
|
||||||
|
it + 1,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
hasUnread = value.unreadCount > 0,
|
||||||
usePanoramaCover = usePanoramaCover,
|
usePanoramaCover = usePanoramaCover,
|
||||||
// KMK <--
|
// KMK <--
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,12 @@ class HistoryScreenModelStateProvider : PreviewParameterProvider<HistoryScreenMo
|
||||||
ogTitle = "Test Title",
|
ogTitle = "Test Title",
|
||||||
// SY <--
|
// SY <--
|
||||||
chapterNumber = Random.nextDouble(),
|
chapterNumber = Random.nextDouble(),
|
||||||
|
// KMK -->
|
||||||
|
read = Random.nextBoolean(),
|
||||||
|
lastPageRead = Random.nextLong(1, 10),
|
||||||
|
totalChapters = Random.nextLong(1, 100),
|
||||||
|
readCount = 1,
|
||||||
|
// KMK <--
|
||||||
readAt = Date.from(Instant.now()),
|
readAt = Date.from(Instant.now()),
|
||||||
readDuration = Random.nextLong(),
|
readDuration = Random.nextLong(),
|
||||||
coverData = MangaCover(
|
coverData = MangaCover(
|
||||||
|
|
|
||||||
|
|
@ -7,10 +7,12 @@ import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.Circle
|
||||||
import androidx.compose.material.icons.outlined.Delete
|
import androidx.compose.material.icons.outlined.Delete
|
||||||
import androidx.compose.material.icons.outlined.FavoriteBorder
|
import androidx.compose.material.icons.outlined.FavoriteBorder
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
|
@ -26,6 +28,7 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
import androidx.compose.ui.tooling.preview.PreviewLightDark
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import eu.kanade.presentation.manga.components.DotSeparatorText
|
||||||
import eu.kanade.presentation.manga.components.MangaCover
|
import eu.kanade.presentation.manga.components.MangaCover
|
||||||
import eu.kanade.presentation.manga.components.MangaCoverHide
|
import eu.kanade.presentation.manga.components.MangaCoverHide
|
||||||
import eu.kanade.presentation.manga.components.RatioSwitchToPanorama
|
import eu.kanade.presentation.manga.components.RatioSwitchToPanorama
|
||||||
|
|
@ -35,6 +38,7 @@ import eu.kanade.tachiyomi.util.lang.toTimestampString
|
||||||
import exh.debug.DebugToggles
|
import exh.debug.DebugToggles
|
||||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||||
import tachiyomi.i18n.MR
|
import tachiyomi.i18n.MR
|
||||||
|
import tachiyomi.presentation.core.components.material.DISABLED_ALPHA
|
||||||
import tachiyomi.presentation.core.components.material.padding
|
import tachiyomi.presentation.core.components.material.padding
|
||||||
import tachiyomi.presentation.core.i18n.stringResource
|
import tachiyomi.presentation.core.i18n.stringResource
|
||||||
|
|
||||||
|
|
@ -49,10 +53,15 @@ fun HistoryItem(
|
||||||
onClickFavorite: () -> Unit,
|
onClickFavorite: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
// KMK -->
|
// KMK -->
|
||||||
|
readProgress: String?,
|
||||||
|
hasUnread: Boolean,
|
||||||
usePanoramaCover: Boolean,
|
usePanoramaCover: Boolean,
|
||||||
coverRatio: MutableFloatState = remember { mutableFloatStateOf(1f) },
|
coverRatio: MutableFloatState = remember { mutableFloatStateOf(1f) },
|
||||||
// KMK <--
|
// KMK <--
|
||||||
) {
|
) {
|
||||||
|
// KMK -->
|
||||||
|
val textAlpha = if (history.read) DISABLED_ALPHA else 1f
|
||||||
|
// KMK <--
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.clickable(onClick = onClickResume)
|
.clickable(onClick = onClickResume)
|
||||||
|
|
@ -111,28 +120,60 @@ fun HistoryItem(
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(start = MaterialTheme.padding.medium, end = MaterialTheme.padding.small),
|
.padding(start = MaterialTheme.padding.medium, end = MaterialTheme.padding.small),
|
||||||
) {
|
) {
|
||||||
val textStyle = MaterialTheme.typography.bodyMedium
|
|
||||||
Text(
|
Text(
|
||||||
text = history.title,
|
text = history.title,
|
||||||
|
// KMK -->
|
||||||
|
color = LocalContentColor.current.copy(alpha = textAlpha),
|
||||||
|
// KMK <--
|
||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
maxLines = 2,
|
maxLines = 2,
|
||||||
overflow = TextOverflow.Ellipsis,
|
overflow = TextOverflow.Ellipsis,
|
||||||
style = textStyle,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
)
|
)
|
||||||
val readAt = remember { history.readAt?.toTimestampString() ?: "" }
|
val readAt = remember { history.readAt?.toTimestampString() ?: "" }
|
||||||
Text(
|
// KMK -->
|
||||||
text = if (history.chapterNumber > -1) {
|
Row(
|
||||||
stringResource(
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
MR.strings.recent_manga_time,
|
|
||||||
formatChapterNumber(history.chapterNumber),
|
|
||||||
readAt,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
readAt
|
|
||||||
},
|
|
||||||
modifier = Modifier.padding(top = 4.dp),
|
modifier = Modifier.padding(top = 4.dp),
|
||||||
style = textStyle,
|
) {
|
||||||
)
|
if (hasUnread) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.Circle,
|
||||||
|
contentDescription = stringResource(MR.strings.unread),
|
||||||
|
modifier = Modifier
|
||||||
|
.height(8.dp)
|
||||||
|
.padding(end = 4.dp),
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// KMK <--
|
||||||
|
Text(
|
||||||
|
text = if (history.chapterNumber > -1) {
|
||||||
|
stringResource(
|
||||||
|
MR.strings.recent_manga_time,
|
||||||
|
formatChapterNumber(history.chapterNumber),
|
||||||
|
readAt,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
readAt
|
||||||
|
},
|
||||||
|
// KMK -->
|
||||||
|
color = LocalContentColor.current.copy(alpha = textAlpha),
|
||||||
|
style = MaterialTheme.typography.bodySmall,
|
||||||
|
// KMK <--
|
||||||
|
)
|
||||||
|
// KMK -->
|
||||||
|
if (readProgress != null) {
|
||||||
|
DotSeparatorText()
|
||||||
|
Text(
|
||||||
|
text = readProgress,
|
||||||
|
maxLines = 1,
|
||||||
|
color = LocalContentColor.current.copy(alpha = textAlpha),
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// KMK <--
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!history.coverData.isMangaFavorite) {
|
if (!history.coverData.isMangaFavorite) {
|
||||||
|
|
@ -169,6 +210,8 @@ private fun HistoryItemPreviews(
|
||||||
onClickResume = {},
|
onClickResume = {},
|
||||||
onClickDelete = {},
|
onClickDelete = {},
|
||||||
onClickFavorite = {},
|
onClickFavorite = {},
|
||||||
|
readProgress = "Page 5",
|
||||||
|
hasUnread = true,
|
||||||
usePanoramaCover = false,
|
usePanoramaCover = false,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,12 @@ internal class HistoryWithRelationsProvider : PreviewParameterProvider<HistoryWi
|
||||||
ogTitle = "Test Title",
|
ogTitle = "Test Title",
|
||||||
// SY <--
|
// SY <--
|
||||||
chapterNumber = 10.2,
|
chapterNumber = 10.2,
|
||||||
|
// KMK -->
|
||||||
|
read = true,
|
||||||
|
lastPageRead = 5,
|
||||||
|
totalChapters = 5L,
|
||||||
|
readCount = 3L,
|
||||||
|
// KMK <--
|
||||||
readAt = Date(1697247357L),
|
readAt = Date(1697247357L),
|
||||||
readDuration = 123L,
|
readDuration = 123L,
|
||||||
coverData = tachiyomi.domain.manga.model.MangaCover(
|
coverData = tachiyomi.domain.manga.model.MangaCover(
|
||||||
|
|
@ -33,6 +39,12 @@ internal class HistoryWithRelationsProvider : PreviewParameterProvider<HistoryWi
|
||||||
ogTitle = "Test Title",
|
ogTitle = "Test Title",
|
||||||
// SY <--
|
// SY <--
|
||||||
chapterNumber = 10.2,
|
chapterNumber = 10.2,
|
||||||
|
// KMK -->
|
||||||
|
read = false,
|
||||||
|
lastPageRead = 5,
|
||||||
|
totalChapters = 5L,
|
||||||
|
readCount = 3L,
|
||||||
|
// KMK <--
|
||||||
readAt = null,
|
readAt = null,
|
||||||
readDuration = 123L,
|
readDuration = 123L,
|
||||||
coverData = tachiyomi.domain.manga.model.MangaCover(
|
coverData = tachiyomi.domain.manga.model.MangaCover(
|
||||||
|
|
@ -52,6 +64,12 @@ internal class HistoryWithRelationsProvider : PreviewParameterProvider<HistoryWi
|
||||||
ogTitle = "Test Title",
|
ogTitle = "Test Title",
|
||||||
// SY <--
|
// SY <--
|
||||||
chapterNumber = -2.0,
|
chapterNumber = -2.0,
|
||||||
|
// KMK -->
|
||||||
|
read = true,
|
||||||
|
lastPageRead = 5,
|
||||||
|
totalChapters = 5L,
|
||||||
|
readCount = 3L,
|
||||||
|
// KMK <--
|
||||||
readAt = Date(1697247357L),
|
readAt = Date(1697247357L),
|
||||||
readDuration = 123L,
|
readDuration = 123L,
|
||||||
coverData = tachiyomi.domain.manga.model.MangaCover(
|
coverData = tachiyomi.domain.manga.model.MangaCover(
|
||||||
|
|
|
||||||
|
|
@ -76,7 +76,11 @@ class MangaBackupCreator(
|
||||||
handler.awaitList {
|
handler.awaitList {
|
||||||
chaptersQueries.getChaptersByMangaId(
|
chaptersQueries.getChaptersByMangaId(
|
||||||
mangaId = manga.id,
|
mangaId = manga.id,
|
||||||
applyScanlatorFilter = 0, // false
|
applyFilter = 0, // false
|
||||||
|
// KMK -->
|
||||||
|
Manga.CHAPTER_SHOW_NOT_BOOKMARKED,
|
||||||
|
Manga.CHAPTER_SHOW_BOOKMARKED,
|
||||||
|
// KMK <--
|
||||||
mapper = backupChapterMapper,
|
mapper = backupChapterMapper,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -250,7 +250,16 @@ class SyncManager(
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun isMangaDifferent(localManga: Manga, remoteManga: BackupManga): Boolean {
|
private suspend fun isMangaDifferent(localManga: Manga, remoteManga: BackupManga): Boolean {
|
||||||
val localChapters = handler.await { chaptersQueries.getChaptersByMangaId(localManga.id, 0).executeAsList() }
|
val localChapters = handler.await {
|
||||||
|
chaptersQueries.getChaptersByMangaId(
|
||||||
|
localManga.id,
|
||||||
|
0,
|
||||||
|
// KMK -->
|
||||||
|
Manga.CHAPTER_SHOW_NOT_BOOKMARKED,
|
||||||
|
Manga.CHAPTER_SHOW_BOOKMARKED,
|
||||||
|
// KMK <--
|
||||||
|
).executeAsList()
|
||||||
|
}
|
||||||
val localCategories = getCategories.await(localManga.id).map { it.order }
|
val localCategories = getCategories.await(localManga.id).map { it.order }
|
||||||
|
|
||||||
if (areChaptersDifferent(localChapters, remoteManga.chapters)) {
|
if (areChaptersDifferent(localChapters, remoteManga.chapters)) {
|
||||||
|
|
|
||||||
|
|
@ -785,9 +785,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, applyScanlatorFilter = true)
|
getMergedChaptersByMangaId.await(manga.id, applyFilter = true)
|
||||||
} else {
|
} else {
|
||||||
getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true)
|
getChaptersByMangaId.await(manga.id, applyFilter = true)
|
||||||
}.getNextUnread(manga, downloadManager, mergedManga)
|
}.getNextUnread(manga, downloadManager, mergedManga)
|
||||||
// SY <--
|
// SY <--
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -311,10 +311,10 @@ class MangaScreenModel(
|
||||||
|
|
||||||
init {
|
init {
|
||||||
screenModelScope.launchIO {
|
screenModelScope.launchIO {
|
||||||
getMangaAndChapters.subscribe(mangaId, applyScanlatorFilter = true).distinctUntilChanged()
|
getMangaAndChapters.subscribe(mangaId, applyFilter = true).distinctUntilChanged()
|
||||||
// SY -->
|
// SY -->
|
||||||
.combine(
|
.combine(
|
||||||
getMergedChaptersByMangaId.subscribe(mangaId, true, applyScanlatorFilter = true)
|
getMergedChaptersByMangaId.subscribe(mangaId, true, applyFilter = true)
|
||||||
.distinctUntilChanged(),
|
.distinctUntilChanged(),
|
||||||
) { (manga, chapters), mergedChapters ->
|
) { (manga, chapters), mergedChapters ->
|
||||||
if (manga.source == MERGED_SOURCE_ID) {
|
if (manga.source == MERGED_SOURCE_ID) {
|
||||||
|
|
@ -447,9 +447,9 @@ class MangaScreenModel(
|
||||||
if (manga.source ==
|
if (manga.source ==
|
||||||
MERGED_SOURCE_ID
|
MERGED_SOURCE_ID
|
||||||
) {
|
) {
|
||||||
getMergedChaptersByMangaId.await(mangaId, applyScanlatorFilter = true)
|
getMergedChaptersByMangaId.await(mangaId, applyFilter = true)
|
||||||
} else {
|
} else {
|
||||||
getMangaAndChapters.awaitChapters(mangaId, applyScanlatorFilter = true)
|
getMangaAndChapters.awaitChapters(mangaId, applyFilter = true)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.toChapterListItems(manga, mergedData)
|
.toChapterListItems(manga, mergedData)
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,7 @@ class ReaderViewModel @JvmOverloads constructor(
|
||||||
|
|
||||||
private val unfilteredChapterList by lazy {
|
private val unfilteredChapterList by lazy {
|
||||||
val manga = manga!!
|
val manga = manga!!
|
||||||
runBlocking { getChaptersByMangaId.await(manga.id, applyScanlatorFilter = false) }
|
runBlocking { getChaptersByMangaId.await(manga.id, applyFilter = false) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -195,11 +195,11 @@ 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, applyScanlatorFilter = true) to
|
getMergedChaptersByMangaId.await(manga.id, applyFilter = true) to
|
||||||
getMergedMangaById.await(manga.id)
|
getMergedMangaById.await(manga.id)
|
||||||
.associateBy { it.id }
|
.associateBy { it.id }
|
||||||
} else {
|
} else {
|
||||||
getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true) to null
|
getChaptersByMangaId.await(manga.id, applyFilter = true) to null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fun isChapterDownloaded(chapter: Chapter): Boolean {
|
fun isChapterDownloaded(chapter: Chapter): Boolean {
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import tachiyomi.data.DatabaseHandler
|
||||||
import tachiyomi.domain.chapter.model.Chapter
|
import tachiyomi.domain.chapter.model.Chapter
|
||||||
import tachiyomi.domain.chapter.model.ChapterUpdate
|
import tachiyomi.domain.chapter.model.ChapterUpdate
|
||||||
import tachiyomi.domain.chapter.repository.ChapterRepository
|
import tachiyomi.domain.chapter.repository.ChapterRepository
|
||||||
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
|
||||||
class ChapterRepositoryImpl(
|
class ChapterRepositoryImpl(
|
||||||
private val handler: DatabaseHandler,
|
private val handler: DatabaseHandler,
|
||||||
|
|
@ -80,9 +81,17 @@ class ChapterRepositoryImpl(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean): List<Chapter> {
|
override suspend fun getChapterByMangaId(mangaId: Long, applyFilter: Boolean): List<Chapter> {
|
||||||
return handler.awaitList {
|
return handler.awaitList {
|
||||||
chaptersQueries.getChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ChapterMapper::mapChapter)
|
chaptersQueries.getChaptersByMangaId(
|
||||||
|
mangaId,
|
||||||
|
applyFilter.toLong(),
|
||||||
|
// KMK -->
|
||||||
|
Manga.CHAPTER_SHOW_NOT_BOOKMARKED,
|
||||||
|
Manga.CHAPTER_SHOW_BOOKMARKED,
|
||||||
|
// KMK <--
|
||||||
|
ChapterMapper::mapChapter,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -111,9 +120,17 @@ class ChapterRepositoryImpl(
|
||||||
return handler.awaitOneOrNull { chaptersQueries.getChapterById(id, ChapterMapper::mapChapter) }
|
return handler.awaitOneOrNull { chaptersQueries.getChapterById(id, ChapterMapper::mapChapter) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean): Flow<List<Chapter>> {
|
override suspend fun getChapterByMangaIdAsFlow(mangaId: Long, applyFilter: Boolean): Flow<List<Chapter>> {
|
||||||
return handler.subscribeToList {
|
return handler.subscribeToList {
|
||||||
chaptersQueries.getChaptersByMangaId(mangaId, applyScanlatorFilter.toLong(), ChapterMapper::mapChapter)
|
chaptersQueries.getChaptersByMangaId(
|
||||||
|
mangaId,
|
||||||
|
applyFilter.toLong(),
|
||||||
|
// KMK -->
|
||||||
|
Manga.CHAPTER_SHOW_NOT_BOOKMARKED,
|
||||||
|
Manga.CHAPTER_SHOW_BOOKMARKED,
|
||||||
|
// KMK <--
|
||||||
|
ChapterMapper::mapChapter,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -132,11 +149,15 @@ class ChapterRepositoryImpl(
|
||||||
return handler.awaitList { chaptersQueries.getChapterByUrl(url, ChapterMapper::mapChapter) }
|
return handler.awaitList { chaptersQueries.getChapterByUrl(url, ChapterMapper::mapChapter) }
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getMergedChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean): List<Chapter> {
|
override suspend fun getMergedChapterByMangaId(mangaId: Long, applyFilter: Boolean): List<Chapter> {
|
||||||
return handler.awaitList {
|
return handler.awaitList {
|
||||||
chaptersQueries.getMergedChaptersByMangaId(
|
chaptersQueries.getMergedChaptersByMangaId(
|
||||||
mangaId,
|
mangaId,
|
||||||
applyScanlatorFilter.toLong(),
|
applyFilter.toLong(),
|
||||||
|
// KMK -->
|
||||||
|
Manga.CHAPTER_SHOW_NOT_BOOKMARKED,
|
||||||
|
Manga.CHAPTER_SHOW_BOOKMARKED,
|
||||||
|
// KMK <--
|
||||||
ChapterMapper::mapChapter,
|
ChapterMapper::mapChapter,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -144,12 +165,16 @@ class ChapterRepositoryImpl(
|
||||||
|
|
||||||
override suspend fun getMergedChapterByMangaIdAsFlow(
|
override suspend fun getMergedChapterByMangaIdAsFlow(
|
||||||
mangaId: Long,
|
mangaId: Long,
|
||||||
applyScanlatorFilter: Boolean,
|
applyFilter: Boolean,
|
||||||
): Flow<List<Chapter>> {
|
): Flow<List<Chapter>> {
|
||||||
return handler.subscribeToList {
|
return handler.subscribeToList {
|
||||||
chaptersQueries.getMergedChaptersByMangaId(
|
chaptersQueries.getMergedChaptersByMangaId(
|
||||||
mangaId,
|
mangaId,
|
||||||
applyScanlatorFilter.toLong(),
|
applyFilter.toLong(),
|
||||||
|
// KMK -->
|
||||||
|
Manga.CHAPTER_SHOW_NOT_BOOKMARKED,
|
||||||
|
Manga.CHAPTER_SHOW_BOOKMARKED,
|
||||||
|
// KMK <--
|
||||||
ChapterMapper::mapChapter,
|
ChapterMapper::mapChapter,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,12 @@ object HistoryMapper {
|
||||||
isFavorite: Boolean,
|
isFavorite: Boolean,
|
||||||
coverLastModified: Long,
|
coverLastModified: Long,
|
||||||
chapterNumber: Double,
|
chapterNumber: Double,
|
||||||
|
// KMK -->
|
||||||
|
read: Boolean,
|
||||||
|
lastPageRead: Long,
|
||||||
|
totalCount: Double,
|
||||||
|
readCount: Double,
|
||||||
|
// KMK <--
|
||||||
readAt: Date?,
|
readAt: Date?,
|
||||||
readDuration: Long,
|
readDuration: Long,
|
||||||
): HistoryWithRelations = HistoryWithRelations(
|
): HistoryWithRelations = HistoryWithRelations(
|
||||||
|
|
@ -38,6 +44,12 @@ object HistoryMapper {
|
||||||
ogTitle = title,
|
ogTitle = title,
|
||||||
// SY <--
|
// SY <--
|
||||||
chapterNumber = chapterNumber,
|
chapterNumber = chapterNumber,
|
||||||
|
// KMK -->
|
||||||
|
read = read,
|
||||||
|
lastPageRead = lastPageRead,
|
||||||
|
totalChapters = totalCount.toLong(),
|
||||||
|
readCount = readCount.toLong(),
|
||||||
|
// KMK <--
|
||||||
readAt = readAt,
|
readAt = readAt,
|
||||||
readDuration = readDuration,
|
readDuration = readDuration,
|
||||||
coverData = MangaCover(
|
coverData = MangaCover(
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import tachiyomi.domain.history.model.History
|
||||||
import tachiyomi.domain.history.model.HistoryUpdate
|
import tachiyomi.domain.history.model.HistoryUpdate
|
||||||
import tachiyomi.domain.history.model.HistoryWithRelations
|
import tachiyomi.domain.history.model.HistoryWithRelations
|
||||||
import tachiyomi.domain.history.repository.HistoryRepository
|
import tachiyomi.domain.history.repository.HistoryRepository
|
||||||
|
import tachiyomi.domain.manga.model.Manga
|
||||||
|
|
||||||
class HistoryRepositoryImpl(
|
class HistoryRepositoryImpl(
|
||||||
private val handler: DatabaseHandler,
|
private val handler: DatabaseHandler,
|
||||||
|
|
@ -15,13 +16,22 @@ class HistoryRepositoryImpl(
|
||||||
|
|
||||||
override fun getHistory(query: String): Flow<List<HistoryWithRelations>> {
|
override fun getHistory(query: String): Flow<List<HistoryWithRelations>> {
|
||||||
return handler.subscribeToList {
|
return handler.subscribeToList {
|
||||||
historyViewQueries.history(query, HistoryMapper::mapHistoryWithRelations)
|
historyViewQueries.history(
|
||||||
|
Manga.CHAPTER_SHOW_NOT_BOOKMARKED,
|
||||||
|
Manga.CHAPTER_SHOW_BOOKMARKED,
|
||||||
|
query,
|
||||||
|
HistoryMapper::mapHistoryWithRelations,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun getLastHistory(): HistoryWithRelations? {
|
override suspend fun getLastHistory(): HistoryWithRelations? {
|
||||||
return handler.awaitOneOrNull {
|
return handler.awaitOneOrNull {
|
||||||
historyViewQueries.getLatestHistory(HistoryMapper::mapHistoryWithRelations)
|
historyViewQueries.getLatestHistory(
|
||||||
|
Manga.CHAPTER_SHOW_NOT_BOOKMARKED,
|
||||||
|
Manga.CHAPTER_SHOW_BOOKMARKED,
|
||||||
|
HistoryMapper::mapHistoryWithRelations,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -101,6 +101,9 @@ object MangaMapper {
|
||||||
chapterFetchedAt: Long,
|
chapterFetchedAt: Long,
|
||||||
lastRead: Long,
|
lastRead: Long,
|
||||||
bookmarkCount: Double,
|
bookmarkCount: Double,
|
||||||
|
// KMK -->
|
||||||
|
bookmarkedReadCount: Long,
|
||||||
|
// KMK <--
|
||||||
category: Long,
|
category: Long,
|
||||||
): LibraryManga = LibraryManga(
|
): LibraryManga = LibraryManga(
|
||||||
manga = mapManga(
|
manga = mapManga(
|
||||||
|
|
@ -137,6 +140,10 @@ object MangaMapper {
|
||||||
totalChapters = totalCount,
|
totalChapters = totalCount,
|
||||||
readCount = readCount.toLong(),
|
readCount = readCount.toLong(),
|
||||||
bookmarkCount = bookmarkCount.toLong(),
|
bookmarkCount = bookmarkCount.toLong(),
|
||||||
|
// KMK -->
|
||||||
|
bookmarkReadCount = bookmarkedReadCount,
|
||||||
|
chapterFlags = chapterFlags,
|
||||||
|
// KMK <--
|
||||||
latestUpload = latestUpload,
|
latestUpload = latestUpload,
|
||||||
chapterFetchedAt = chapterFetchedAt,
|
chapterFetchedAt = chapterFetchedAt,
|
||||||
lastRead = lastRead,
|
lastRead = lastRead,
|
||||||
|
|
|
||||||
|
|
@ -56,13 +56,22 @@ WHERE _id = :id;
|
||||||
getChaptersByMangaId:
|
getChaptersByMangaId:
|
||||||
SELECT C.*
|
SELECT C.*
|
||||||
FROM chapters C
|
FROM chapters C
|
||||||
|
-- KMK -->
|
||||||
|
JOIN mangas M ON C.manga_id = M._id
|
||||||
|
-- KMK <--
|
||||||
LEFT JOIN excluded_scanlators ES
|
LEFT JOIN excluded_scanlators ES
|
||||||
ON C.manga_id = ES.manga_id
|
ON C.manga_id = ES.manga_id
|
||||||
AND C.scanlator = ES.scanlator
|
AND C.scanlator = ES.scanlator
|
||||||
WHERE C.manga_id = :mangaId
|
WHERE C.manga_id = :mangaId
|
||||||
AND (
|
AND (
|
||||||
:applyScanlatorFilter = 0
|
:applyFilter = 0
|
||||||
OR ES.scanlator IS NULL
|
-- KMK -->
|
||||||
|
OR (
|
||||||
|
ES.scanlator IS NULL
|
||||||
|
AND ((M.chapter_flags & :bookmarkUnmask) = 0 OR C.bookmark = 0)
|
||||||
|
AND ((M.chapter_flags & :bookmarkMask) = 0 OR C.bookmark = 1)
|
||||||
|
)
|
||||||
|
-- KMK <--
|
||||||
);
|
);
|
||||||
|
|
||||||
getScanlatorsByMangaId:
|
getScanlatorsByMangaId:
|
||||||
|
|
@ -97,10 +106,19 @@ ON C.manga_id = M.manga_id
|
||||||
LEFT JOIN excluded_scanlators ES
|
LEFT JOIN excluded_scanlators ES
|
||||||
ON M.merge_id = ES.manga_id
|
ON M.merge_id = ES.manga_id
|
||||||
AND C.scanlator = ES.scanlator
|
AND C.scanlator = ES.scanlator
|
||||||
|
-- KMK -->
|
||||||
|
JOIN mangas MA ON MA._id = :mangaId
|
||||||
|
-- KMK <--
|
||||||
WHERE M.merge_id = :mangaId
|
WHERE M.merge_id = :mangaId
|
||||||
AND (
|
AND (
|
||||||
:applyScanlatorFilter = 0
|
:applyFilter = 0
|
||||||
OR ES.scanlator IS NULL
|
-- KMK -->
|
||||||
|
OR (
|
||||||
|
ES.scanlator IS NULL
|
||||||
|
AND ((MA.chapter_flags & :bookmarkUnmask) = 0 OR C.bookmark = 0)
|
||||||
|
AND ((MA.chapter_flags & :bookmarkMask) = 0 OR C.bookmark = 1)
|
||||||
|
)
|
||||||
|
-- KMK <--
|
||||||
);
|
);
|
||||||
|
|
||||||
getScanlatorsByMergeId:
|
getScanlatorsByMergeId:
|
||||||
|
|
@ -142,4 +160,4 @@ SET manga_id = coalesce(:mangaId, manga_id),
|
||||||
WHERE _id = :chapterId;
|
WHERE _id = :chapterId;
|
||||||
|
|
||||||
selectLastInsertedRowId:
|
selectLastInsertedRowId:
|
||||||
SELECT last_insert_rowid();
|
SELECT last_insert_rowid();
|
||||||
|
|
|
||||||
147
data/src/main/sqldelight/tachiyomi/migrations/40.sqm
Normal file
147
data/src/main/sqldelight/tachiyomi/migrations/40.sqm
Normal file
|
|
@ -0,0 +1,147 @@
|
||||||
|
DROP VIEW IF EXISTS historyView;
|
||||||
|
|
||||||
|
CREATE VIEW historyView AS
|
||||||
|
SELECT
|
||||||
|
history._id AS id,
|
||||||
|
mangas._id AS mangaId,
|
||||||
|
chapters._id AS chapterId,
|
||||||
|
mangas.title,
|
||||||
|
mangas.thumbnail_url AS thumbnailUrl,
|
||||||
|
mangas.source,
|
||||||
|
mangas.favorite,
|
||||||
|
mangas.cover_last_modified,
|
||||||
|
chapters.chapter_number AS chapterNumber,
|
||||||
|
-- KMK -->
|
||||||
|
mangas.chapter_flags AS chapterFlags,
|
||||||
|
chapters.read AS read,
|
||||||
|
chapters.last_page_read AS lastPageRead,
|
||||||
|
coalesce(C.total, 0) AS totalCount,
|
||||||
|
coalesce(C.readCount, 0) AS readCount,
|
||||||
|
coalesce(C.bookmarkCount, 0) AS bookmarkCount,
|
||||||
|
coalesce(C.bookmarkReadCount, 0) AS bookmarkReadCount,
|
||||||
|
-- KMK <--
|
||||||
|
history.last_read AS readAt,
|
||||||
|
history.time_read AS readDuration,
|
||||||
|
max_last_read.last_read AS maxReadAt,
|
||||||
|
max_last_read.chapter_id AS maxReadAtChapterId
|
||||||
|
FROM mangas
|
||||||
|
JOIN chapters
|
||||||
|
ON mangas._id = chapters.manga_id
|
||||||
|
JOIN history
|
||||||
|
ON chapters._id = history.chapter_id
|
||||||
|
JOIN (
|
||||||
|
SELECT chapters.manga_id,chapters._id AS chapter_id, MAX(history.last_read) AS last_read
|
||||||
|
FROM chapters JOIN history
|
||||||
|
ON chapters._id = history.chapter_id
|
||||||
|
GROUP BY chapters.manga_id
|
||||||
|
) AS max_last_read
|
||||||
|
ON chapters.manga_id = max_last_read.manga_id
|
||||||
|
-- KMK -->
|
||||||
|
LEFT JOIN(
|
||||||
|
SELECT
|
||||||
|
chapters.manga_id,
|
||||||
|
count(*) AS total,
|
||||||
|
sum(read) AS readCount,
|
||||||
|
sum(bookmark) AS bookmarkCount,
|
||||||
|
sum(CASE WHEN bookmark = 1 AND read = 1 THEN 1 ELSE 0 END) AS bookmarkReadCount,
|
||||||
|
excluded_scanlators.scanlator AS ex_scanlator
|
||||||
|
FROM chapters
|
||||||
|
LEFT JOIN excluded_scanlators
|
||||||
|
ON chapters.manga_id = excluded_scanlators.manga_id
|
||||||
|
AND chapters.scanlator = excluded_scanlators.scanlator
|
||||||
|
WHERE ex_scanlator IS NULL
|
||||||
|
GROUP BY chapters.manga_id
|
||||||
|
) AS C
|
||||||
|
ON mangas._id = C.manga_id;
|
||||||
|
-- KMK <--
|
||||||
|
|
||||||
|
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,
|
||||||
|
-- KMK -->
|
||||||
|
coalesce(C.bookmarkReadCount, 0) AS bookmarkedReadCount,
|
||||||
|
-- KMK <--
|
||||||
|
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,
|
||||||
|
-- KMK -->
|
||||||
|
sum(CASE WHEN chapters.bookmark = 1 AND chapters.read = 1 THEN 1 ELSE 0 END) AS bookmarkReadCount,
|
||||||
|
-- KMK <--
|
||||||
|
excluded_scanlators.scanlator AS ex_scanlator
|
||||||
|
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 ex_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.source <> 6969
|
||||||
|
UNION
|
||||||
|
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,
|
||||||
|
-- KMK -->
|
||||||
|
coalesce(C.bookmarkReadCount, 0) AS bookmarkedReadCount,
|
||||||
|
-- KMK <--
|
||||||
|
coalesce(MC.category_id, 0) AS category
|
||||||
|
FROM mangas M
|
||||||
|
LEFT JOIN (
|
||||||
|
SELECT merged.manga_id,merged.merge_id
|
||||||
|
FROM merged
|
||||||
|
GROUP BY merged.merge_id
|
||||||
|
) AS ME
|
||||||
|
ON ME.merge_id = M._id
|
||||||
|
LEFT JOIN(
|
||||||
|
SELECT
|
||||||
|
ME.merge_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,
|
||||||
|
-- KMK -->
|
||||||
|
sum(CASE WHEN chapters.bookmark = 1 AND chapters.read = 1 THEN 1 ELSE 0 END) AS bookmarkReadCount,
|
||||||
|
-- KMK <--
|
||||||
|
excluded_scanlators.scanlator AS ex_scanlator
|
||||||
|
FROM chapters
|
||||||
|
LEFT JOIN excluded_scanlators
|
||||||
|
ON chapters.manga_id = excluded_scanlators.manga_id
|
||||||
|
AND chapters.scanlator = excluded_scanlators.scanlator
|
||||||
|
LEFT JOIN history
|
||||||
|
ON chapters._id = history.chapter_id
|
||||||
|
LEFT JOIN merged ME
|
||||||
|
ON ME.manga_id = chapters.manga_id
|
||||||
|
WHERE ex_scanlator IS NULL
|
||||||
|
GROUP BY ME.merge_id
|
||||||
|
) AS C
|
||||||
|
ON M._id = C.merge_id -- ON ME.merge_id = C.merge_id
|
||||||
|
LEFT JOIN mangas_categories AS MC
|
||||||
|
ON MC.manga_id = M._id
|
||||||
|
WHERE M.source = 6969;
|
||||||
|
|
@ -9,6 +9,15 @@ SELECT
|
||||||
mangas.favorite,
|
mangas.favorite,
|
||||||
mangas.cover_last_modified,
|
mangas.cover_last_modified,
|
||||||
chapters.chapter_number AS chapterNumber,
|
chapters.chapter_number AS chapterNumber,
|
||||||
|
-- KMK -->
|
||||||
|
mangas.chapter_flags AS chapterFlags,
|
||||||
|
chapters.read AS read,
|
||||||
|
chapters.last_page_read AS lastPageRead,
|
||||||
|
coalesce(C.total, 0) AS totalCount,
|
||||||
|
coalesce(C.readCount, 0) AS readCount,
|
||||||
|
coalesce(C.bookmarkCount, 0) AS bookmarkCount,
|
||||||
|
coalesce(C.bookmarkReadCount, 0) AS bookmarkReadCount,
|
||||||
|
-- KMK <--
|
||||||
history.last_read AS readAt,
|
history.last_read AS readAt,
|
||||||
history.time_read AS readDuration,
|
history.time_read AS readDuration,
|
||||||
max_last_read.last_read AS maxReadAt,
|
max_last_read.last_read AS maxReadAt,
|
||||||
|
|
@ -24,7 +33,25 @@ JOIN (
|
||||||
ON chapters._id = history.chapter_id
|
ON chapters._id = history.chapter_id
|
||||||
GROUP BY chapters.manga_id
|
GROUP BY chapters.manga_id
|
||||||
) AS max_last_read
|
) AS max_last_read
|
||||||
ON chapters.manga_id = max_last_read.manga_id;
|
ON chapters.manga_id = max_last_read.manga_id
|
||||||
|
-- KMK -->
|
||||||
|
LEFT JOIN(
|
||||||
|
SELECT
|
||||||
|
chapters.manga_id,
|
||||||
|
count(*) AS total,
|
||||||
|
sum(read) AS readCount,
|
||||||
|
sum(bookmark) AS bookmarkCount,
|
||||||
|
sum(CASE WHEN bookmark = 1 AND read = 1 THEN 1 ELSE 0 END) AS bookmarkReadCount,
|
||||||
|
excluded_scanlators.scanlator AS ex_scanlator
|
||||||
|
FROM chapters
|
||||||
|
LEFT JOIN excluded_scanlators
|
||||||
|
ON chapters.manga_id = excluded_scanlators.manga_id
|
||||||
|
AND chapters.scanlator = excluded_scanlators.scanlator
|
||||||
|
WHERE ex_scanlator IS NULL
|
||||||
|
GROUP BY chapters.manga_id
|
||||||
|
) AS C
|
||||||
|
ON mangas._id = C.manga_id;
|
||||||
|
-- KMK <--
|
||||||
|
|
||||||
history:
|
history:
|
||||||
SELECT
|
SELECT
|
||||||
|
|
@ -37,6 +64,20 @@ source,
|
||||||
favorite,
|
favorite,
|
||||||
cover_last_modified,
|
cover_last_modified,
|
||||||
chapterNumber,
|
chapterNumber,
|
||||||
|
-- KMK -->
|
||||||
|
read,
|
||||||
|
lastPageRead,
|
||||||
|
CASE
|
||||||
|
WHEN (chapterFlags & :bookmarkUnmask) != 0 THEN totalCount - bookmarkCount
|
||||||
|
WHEN (chapterFlags & :bookmarkMask) != 0 THEN bookmarkCount
|
||||||
|
ELSE totalCount
|
||||||
|
END AS totalCount,
|
||||||
|
CASE
|
||||||
|
WHEN (chapterFlags & :bookmarkUnmask) != 0 THEN readCount - bookmarkReadCount
|
||||||
|
WHEN (chapterFlags & :bookmarkMask) != 0 THEN bookmarkReadCount
|
||||||
|
ELSE readCount
|
||||||
|
END AS readCount,
|
||||||
|
-- KMK <--
|
||||||
readAt,
|
readAt,
|
||||||
readDuration
|
readDuration
|
||||||
FROM historyView
|
FROM historyView
|
||||||
|
|
@ -56,6 +97,20 @@ source,
|
||||||
favorite,
|
favorite,
|
||||||
cover_last_modified,
|
cover_last_modified,
|
||||||
chapterNumber,
|
chapterNumber,
|
||||||
|
-- KMK -->
|
||||||
|
read,
|
||||||
|
lastPageRead,
|
||||||
|
CASE
|
||||||
|
WHEN (chapterFlags & :bookmarkUnmask) != 0 THEN totalCount - bookmarkCount
|
||||||
|
WHEN (chapterFlags & :bookmarkMask) != 0 THEN bookmarkCount
|
||||||
|
ELSE totalCount
|
||||||
|
END AS totalCount,
|
||||||
|
CASE
|
||||||
|
WHEN (chapterFlags & :bookmarkUnmask) != 0 THEN readCount - bookmarkReadCount
|
||||||
|
WHEN (chapterFlags & :bookmarkMask) != 0 THEN bookmarkReadCount
|
||||||
|
ELSE readCount
|
||||||
|
END AS readCount,
|
||||||
|
-- KMK <--
|
||||||
readAt,
|
readAt,
|
||||||
readDuration
|
readDuration
|
||||||
FROM historyView
|
FROM historyView
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,9 @@ SELECT
|
||||||
coalesce(C.fetchedAt, 0) AS chapterFetchedAt,
|
coalesce(C.fetchedAt, 0) AS chapterFetchedAt,
|
||||||
coalesce(C.lastRead, 0) AS lastRead,
|
coalesce(C.lastRead, 0) AS lastRead,
|
||||||
coalesce(C.bookmarkCount, 0) AS bookmarkCount,
|
coalesce(C.bookmarkCount, 0) AS bookmarkCount,
|
||||||
|
-- KMK -->
|
||||||
|
coalesce(C.bookmarkReadCount, 0) AS bookmarkedReadCount,
|
||||||
|
-- KMK <--
|
||||||
coalesce(MC.category_id, 0) AS category
|
coalesce(MC.category_id, 0) AS category
|
||||||
FROM mangas M
|
FROM mangas M
|
||||||
LEFT JOIN(
|
LEFT JOIN(
|
||||||
|
|
@ -19,6 +22,9 @@ LEFT JOIN(
|
||||||
coalesce(max(history.last_read), 0) AS lastRead,
|
coalesce(max(history.last_read), 0) AS lastRead,
|
||||||
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,
|
||||||
|
-- KMK -->
|
||||||
|
sum(CASE WHEN chapters.bookmark = 1 AND chapters.read = 1 THEN 1 ELSE 0 END) AS bookmarkReadCount,
|
||||||
|
-- KMK <--
|
||||||
excluded_scanlators.scanlator AS ex_scanlator
|
excluded_scanlators.scanlator AS ex_scanlator
|
||||||
FROM chapters
|
FROM chapters
|
||||||
LEFT JOIN excluded_scanlators
|
LEFT JOIN excluded_scanlators
|
||||||
|
|
@ -42,6 +48,9 @@ SELECT
|
||||||
coalesce(C.fetchedAt, 0) AS chapterFetchedAt,
|
coalesce(C.fetchedAt, 0) AS chapterFetchedAt,
|
||||||
coalesce(C.lastRead, 0) AS lastRead,
|
coalesce(C.lastRead, 0) AS lastRead,
|
||||||
coalesce(C.bookmarkCount, 0) AS bookmarkCount,
|
coalesce(C.bookmarkCount, 0) AS bookmarkCount,
|
||||||
|
-- KMK -->
|
||||||
|
coalesce(C.bookmarkReadCount, 0) AS bookmarkedReadCount,
|
||||||
|
-- KMK <--
|
||||||
coalesce(MC.category_id, 0) AS category
|
coalesce(MC.category_id, 0) AS category
|
||||||
FROM mangas M
|
FROM mangas M
|
||||||
LEFT JOIN (
|
LEFT JOIN (
|
||||||
|
|
@ -59,6 +68,9 @@ LEFT JOIN(
|
||||||
coalesce(max(history.last_read), 0) AS lastRead,
|
coalesce(max(history.last_read), 0) AS lastRead,
|
||||||
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,
|
||||||
|
-- KMK -->
|
||||||
|
sum(CASE WHEN chapters.bookmark = 1 AND chapters.read = 1 THEN 1 ELSE 0 END) AS bookmarkReadCount,
|
||||||
|
-- KMK <--
|
||||||
excluded_scanlators.scanlator AS ex_scanlator
|
excluded_scanlators.scanlator AS ex_scanlator
|
||||||
FROM chapters
|
FROM chapters
|
||||||
LEFT JOIN excluded_scanlators
|
LEFT JOIN excluded_scanlators
|
||||||
|
|
@ -84,4 +96,4 @@ WHERE libraryView.favorite = 1;
|
||||||
readMangaNonLibrary:
|
readMangaNonLibrary:
|
||||||
SELECT *
|
SELECT *
|
||||||
FROM libraryView
|
FROM libraryView
|
||||||
WHERE libraryView.favorite = 0 AND libraryView.readCount != 0;
|
WHERE libraryView.favorite = 0 AND libraryView.readCount != 0;
|
||||||
|
|
|
||||||
|
|
@ -9,9 +9,9 @@ class GetChaptersByMangaId(
|
||||||
private val chapterRepository: ChapterRepository,
|
private val chapterRepository: ChapterRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun await(mangaId: Long, applyScanlatorFilter: Boolean = false): List<Chapter> {
|
suspend fun await(mangaId: Long, applyFilter: Boolean = false): List<Chapter> {
|
||||||
return try {
|
return try {
|
||||||
chapterRepository.getChapterByMangaId(mangaId, applyScanlatorFilter)
|
chapterRepository.getChapterByMangaId(mangaId, applyFilter)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
emptyList()
|
emptyList()
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,11 @@ class GetMergedChaptersByMangaId(
|
||||||
suspend fun await(
|
suspend fun await(
|
||||||
mangaId: Long,
|
mangaId: Long,
|
||||||
dedupe: Boolean = true,
|
dedupe: Boolean = true,
|
||||||
applyScanlatorFilter: Boolean = false,
|
applyFilter: Boolean = false,
|
||||||
): List<Chapter> {
|
): List<Chapter> {
|
||||||
return transformMergedChapters(
|
return transformMergedChapters(
|
||||||
getMergedReferencesById.await(mangaId),
|
getMergedReferencesById.await(mangaId),
|
||||||
getFromDatabase(mangaId, applyScanlatorFilter),
|
getFromDatabase(mangaId, applyFilter),
|
||||||
dedupe,
|
dedupe,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -31,10 +31,10 @@ class GetMergedChaptersByMangaId(
|
||||||
suspend fun subscribe(
|
suspend fun subscribe(
|
||||||
mangaId: Long,
|
mangaId: Long,
|
||||||
dedupe: Boolean = true,
|
dedupe: Boolean = true,
|
||||||
applyScanlatorFilter: Boolean = false,
|
applyFilter: Boolean = false,
|
||||||
): Flow<List<Chapter>> {
|
): Flow<List<Chapter>> {
|
||||||
return try {
|
return try {
|
||||||
chapterRepository.getMergedChapterByMangaIdAsFlow(mangaId, applyScanlatorFilter)
|
chapterRepository.getMergedChapterByMangaIdAsFlow(mangaId, applyFilter)
|
||||||
.combine(getMergedReferencesById.subscribe(mangaId)) { chapters, references ->
|
.combine(getMergedReferencesById.subscribe(mangaId)) { chapters, references ->
|
||||||
transformMergedChapters(references, chapters, dedupe)
|
transformMergedChapters(references, chapters, dedupe)
|
||||||
}
|
}
|
||||||
|
|
@ -46,10 +46,10 @@ class GetMergedChaptersByMangaId(
|
||||||
|
|
||||||
private suspend fun getFromDatabase(
|
private suspend fun getFromDatabase(
|
||||||
mangaId: Long,
|
mangaId: Long,
|
||||||
applyScanlatorFilter: Boolean = false,
|
applyFilter: Boolean = false,
|
||||||
): List<Chapter> {
|
): List<Chapter> {
|
||||||
return try {
|
return try {
|
||||||
chapterRepository.getMergedChapterByMangaId(mangaId, applyScanlatorFilter)
|
chapterRepository.getMergedChapterByMangaId(mangaId, applyFilter)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
logcat(LogPriority.ERROR, e)
|
logcat(LogPriority.ERROR, e)
|
||||||
emptyList()
|
emptyList()
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ interface ChapterRepository {
|
||||||
|
|
||||||
suspend fun removeChaptersWithIds(chapterIds: List<Long>)
|
suspend fun removeChaptersWithIds(chapterIds: List<Long>)
|
||||||
|
|
||||||
suspend fun getChapterByMangaId(mangaId: Long, applyScanlatorFilter: Boolean = false): List<Chapter>
|
suspend fun getChapterByMangaId(mangaId: Long, applyFilter: Boolean = false): List<Chapter>
|
||||||
|
|
||||||
suspend fun getScanlatorsByMangaId(mangaId: Long): List<String>
|
suspend fun getScanlatorsByMangaId(mangaId: Long): List<String>
|
||||||
|
|
||||||
|
|
@ -24,18 +24,18 @@ interface ChapterRepository {
|
||||||
|
|
||||||
suspend fun getChapterById(id: Long): Chapter?
|
suspend fun getChapterById(id: Long): Chapter?
|
||||||
|
|
||||||
suspend fun getChapterByMangaIdAsFlow(mangaId: Long, applyScanlatorFilter: Boolean = false): Flow<List<Chapter>>
|
suspend fun getChapterByMangaIdAsFlow(mangaId: Long, applyFilter: 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, applyScanlatorFilter: Boolean = false): List<Chapter>
|
suspend fun getMergedChapterByMangaId(mangaId: Long, applyFilter: Boolean = false): List<Chapter>
|
||||||
|
|
||||||
suspend fun getMergedChapterByMangaIdAsFlow(
|
suspend fun getMergedChapterByMangaIdAsFlow(
|
||||||
mangaId: Long,
|
mangaId: Long,
|
||||||
applyScanlatorFilter: Boolean = false,
|
applyFilter: Boolean = false,
|
||||||
): Flow<List<Chapter>>
|
): Flow<List<Chapter>>
|
||||||
|
|
||||||
suspend fun getScanlatorsByMergeId(mangaId: Long): List<String>
|
suspend fun getScanlatorsByMergeId(mangaId: Long): List<String>
|
||||||
|
|
|
||||||
|
|
@ -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, applyScanlatorFilter = true)
|
val chapters = getMergedChaptersByMangaId.await(mangaId, applyFilter = 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, applyScanlatorFilter = true)
|
val chapters = getChaptersByMangaId.await(mangaId, applyFilter = 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, applyScanlatorFilter = true)
|
val chapters = getChaptersByMangaId.await(mangaId, applyFilter = true)
|
||||||
.sortedWith(getChapterSort(manga, sortDescending = false))
|
.sortedWith(getChapterSort(manga, sortDescending = false))
|
||||||
|
|
||||||
return if (onlyUnread) {
|
return if (onlyUnread) {
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,12 @@ data class HistoryWithRelations(
|
||||||
val ogTitle: String,
|
val ogTitle: String,
|
||||||
// SY <--
|
// SY <--
|
||||||
val chapterNumber: Double,
|
val chapterNumber: Double,
|
||||||
|
// KMK -->
|
||||||
|
val read: Boolean,
|
||||||
|
val lastPageRead: Long,
|
||||||
|
val totalChapters: Long,
|
||||||
|
val readCount: Long,
|
||||||
|
// KMK <--
|
||||||
val readAt: Date?,
|
val readAt: Date?,
|
||||||
val readDuration: Long,
|
val readDuration: Long,
|
||||||
val coverData: MangaCover,
|
val coverData: MangaCover,
|
||||||
|
|
@ -24,4 +30,9 @@ data class HistoryWithRelations(
|
||||||
private val customMangaManager: GetCustomMangaInfo by injectLazy()
|
private val customMangaManager: GetCustomMangaInfo by injectLazy()
|
||||||
}
|
}
|
||||||
// SY <--
|
// SY <--
|
||||||
|
|
||||||
|
// KMK -->
|
||||||
|
val unreadCount
|
||||||
|
get() = totalChapters - readCount
|
||||||
|
// KMK <--
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,10 @@ data class LibraryManga(
|
||||||
val totalChapters: Long,
|
val totalChapters: Long,
|
||||||
val readCount: Long,
|
val readCount: Long,
|
||||||
val bookmarkCount: Long,
|
val bookmarkCount: Long,
|
||||||
|
// KMK -->
|
||||||
|
val bookmarkReadCount: Long,
|
||||||
|
val chapterFlags: Long,
|
||||||
|
// KMK <--
|
||||||
val latestUpload: Long,
|
val latestUpload: Long,
|
||||||
val chapterFetchedAt: Long,
|
val chapterFetchedAt: Long,
|
||||||
val lastRead: Long,
|
val lastRead: Long,
|
||||||
|
|
@ -15,7 +19,13 @@ data class LibraryManga(
|
||||||
val id: Long = manga.id
|
val id: Long = manga.id
|
||||||
|
|
||||||
val unreadCount
|
val unreadCount
|
||||||
get() = totalChapters - readCount
|
get() = when {
|
||||||
|
// KMK -->
|
||||||
|
chapterFlags and Manga.CHAPTER_SHOW_NOT_BOOKMARKED != 0L -> totalChapters - bookmarkCount - (readCount - bookmarkReadCount)
|
||||||
|
chapterFlags and Manga.CHAPTER_SHOW_BOOKMARKED != 0L -> bookmarkCount - bookmarkReadCount
|
||||||
|
// KMK <--
|
||||||
|
else -> totalChapters - readCount
|
||||||
|
}
|
||||||
|
|
||||||
val hasBookmarks
|
val hasBookmarks
|
||||||
get() = bookmarkCount > 0
|
get() = bookmarkCount > 0
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@ class FetchInterval(
|
||||||
window: Pair<Long, Long>,
|
window: Pair<Long, Long>,
|
||||||
): MangaUpdate {
|
): MangaUpdate {
|
||||||
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(
|
val interval = manga.fetchInterval.takeIf { it < 0 } ?: calculateInterval(
|
||||||
chapters = getChaptersByMangaId.await(manga.id, applyScanlatorFilter = true),
|
chapters = getChaptersByMangaId.await(manga.id, applyFilter = true),
|
||||||
zone = dateTime.zone,
|
zone = dateTime.zone,
|
||||||
)
|
)
|
||||||
val currentWindow = if (window.first == 0L && window.second == 0L) {
|
val currentWindow = if (window.first == 0L && window.second == 0L) {
|
||||||
|
|
|
||||||
|
|
@ -12,10 +12,10 @@ class GetMangaWithChapters(
|
||||||
private val chapterRepository: ChapterRepository,
|
private val chapterRepository: ChapterRepository,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
suspend fun subscribe(id: Long, applyScanlatorFilter: Boolean = false): Flow<Pair<Manga, List<Chapter>>> {
|
suspend fun subscribe(id: Long, applyFilter: Boolean = false): Flow<Pair<Manga, List<Chapter>>> {
|
||||||
return combine(
|
return combine(
|
||||||
mangaRepository.getMangaByIdAsFlow(id),
|
mangaRepository.getMangaByIdAsFlow(id),
|
||||||
chapterRepository.getChapterByMangaIdAsFlow(id, applyScanlatorFilter),
|
chapterRepository.getChapterByMangaIdAsFlow(id, applyFilter),
|
||||||
) { 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, applyScanlatorFilter: Boolean = false): List<Chapter> {
|
suspend fun awaitChapters(id: Long, applyFilter: Boolean = false): List<Chapter> {
|
||||||
return chapterRepository.getChapterByMangaId(id, applyScanlatorFilter)
|
return chapterRepository.getChapterByMangaId(id, applyFilter)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue