Allow partially read chapters to be marked as unread in updates screen (#8884)

* Allow partially read chapters to be marked as unread in updates screen

* Review changes

* Review changes 2

(cherry picked from commit f301dc64f00c2d8a19cb89610cf6fba7eb78917d)

# Conflicts:
#	app/src/main/java/eu/kanade/presentation/manga/MangaScreen.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaScreenModel.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/updates/UpdatesScreenModel.kt
#	app/src/main/sqldelight/migrations/23.sqm
This commit is contained in:
zbue 2023-01-15 07:26:40 +08:00 committed by Jobobby04
parent 7f7789792b
commit 740b3e4616
13 changed files with 178 additions and 79 deletions

View File

@ -31,7 +31,7 @@ android {
applicationId = "eu.kanade.tachiyomi.sy" applicationId = "eu.kanade.tachiyomi.sy"
minSdk = AndroidConfig.minSdk minSdk = AndroidConfig.minSdk
targetSdk = AndroidConfig.targetSdk targetSdk = AndroidConfig.targetSdk
versionCode = 46 versionCode = 47
versionName = "1.9.0" versionName = "1.9.0"
buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"") buildConfigField("String", "COMMIT_COUNT", "\"${getCommitCount()}\"")

View File

@ -3,8 +3,8 @@ package eu.kanade.data.updates
import eu.kanade.domain.manga.model.MangaCover import eu.kanade.domain.manga.model.MangaCover
import eu.kanade.domain.updates.model.UpdatesWithRelations import eu.kanade.domain.updates.model.UpdatesWithRelations
val updateWithRelationMapper: (Long, String, Long, String, String?, Boolean, Boolean, Long, Boolean, String?, Long, Long, Long) -> UpdatesWithRelations = { val updateWithRelationMapper: (Long, String, Long, String, String?, Boolean, Boolean, Long, Long, Boolean, String?, Long, Long, Long) -> UpdatesWithRelations = {
mangaId, mangaTitle, chapterId, chapterName, scanlator, read, bookmark, sourceId, favorite, thumbnailUrl, coverLastModified, _, dateFetch -> mangaId, mangaTitle, chapterId, chapterName, scanlator, read, bookmark, lastPageRead, sourceId, favorite, thumbnailUrl, coverLastModified, _, dateFetch ->
UpdatesWithRelations( UpdatesWithRelations(
mangaId = mangaId, mangaId = mangaId,
// SY --> // SY -->
@ -15,6 +15,7 @@ val updateWithRelationMapper: (Long, String, Long, String, String?, Boolean, Boo
scanlator = scanlator, scanlator = scanlator,
read = read, read = read,
bookmark = bookmark, bookmark = bookmark,
lastPageRead = lastPageRead,
sourceId = sourceId, sourceId = sourceId,
dateFetch = dateFetch, dateFetch = dateFetch,
coverData = MangaCover( coverData = MangaCover(

View File

@ -17,11 +17,12 @@ private val mapper = { cursor: SqlCursor ->
cursor.getLong(5)!! == 1L, cursor.getLong(5)!! == 1L,
cursor.getLong(6)!! == 1L, cursor.getLong(6)!! == 1L,
cursor.getLong(7)!!, cursor.getLong(7)!!,
cursor.getLong(8)!! == 1L, cursor.getLong(8)!!,
cursor.getString(9), cursor.getLong(9)!! == 1L,
cursor.getLong(10)!!, cursor.getString(10),
cursor.getLong(11)!!, cursor.getLong(11)!!,
cursor.getLong(12)!!, cursor.getLong(12)!!,
cursor.getLong(13)!!,
) )
} }
@ -38,6 +39,7 @@ class UpdatesQuery(val driver: SqlDriver, val after: Long) : Query<UpdatesWithRe
chapters.scanlator, chapters.scanlator,
chapters.read, chapters.read,
chapters.bookmark, chapters.bookmark,
chapters.last_page_read,
mangas.source, mangas.source,
mangas.favorite, mangas.favorite,
mangas.thumbnail_url AS thumbnailUrl, mangas.thumbnail_url AS thumbnailUrl,
@ -58,6 +60,7 @@ class UpdatesQuery(val driver: SqlDriver, val after: Long) : Query<UpdatesWithRe
chapters.scanlator, chapters.scanlator,
chapters.read, chapters.read,
chapters.bookmark, chapters.bookmark,
chapters.last_page_read,
mangas.source, mangas.source,
mangas.favorite, mangas.favorite,
mangas.thumbnail_url AS thumbnailUrl, mangas.thumbnail_url AS thumbnailUrl,

View File

@ -14,6 +14,7 @@ data class UpdatesWithRelations(
val scanlator: String?, val scanlator: String?,
val read: Boolean, val read: Boolean,
val bookmark: Boolean, val bookmark: Boolean,
val lastPageRead: Long,
val sourceId: Long, val sourceId: Long,
val dateFetch: Long, val dateFetch: Long,
val coverData: MangaCover, val coverData: MangaCover,

View File

@ -39,6 +39,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedbackType import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalHapticFeedback import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
@ -47,6 +48,7 @@ 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.chapter.model.Chapter import eu.kanade.domain.chapter.model.Chapter
import eu.kanade.domain.manga.model.Manga
import eu.kanade.presentation.components.ChapterDownloadAction import eu.kanade.presentation.components.ChapterDownloadAction
import eu.kanade.presentation.components.ExtendedFloatingActionButton import eu.kanade.presentation.components.ExtendedFloatingActionButton
import eu.kanade.presentation.components.LazyColumn import eu.kanade.presentation.components.LazyColumn
@ -82,8 +84,12 @@ import eu.kanade.tachiyomi.source.online.english.Tsumino
import eu.kanade.tachiyomi.ui.manga.ChapterItem import eu.kanade.tachiyomi.ui.manga.ChapterItem
import eu.kanade.tachiyomi.ui.manga.MangaScreenState import eu.kanade.tachiyomi.ui.manga.MangaScreenState
import eu.kanade.tachiyomi.ui.manga.PagePreviewState import eu.kanade.tachiyomi.ui.manga.PagePreviewState
import eu.kanade.tachiyomi.ui.manga.chapterDecimalFormat
import eu.kanade.tachiyomi.util.lang.toRelativeString
import exh.metadata.MetadataUtil
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
import exh.source.getMainSource import exh.source.getMainSource
import exh.source.isEhBasedManga
import exh.ui.metadata.adapters.EHentaiDescription import exh.ui.metadata.adapters.EHentaiDescription
import exh.ui.metadata.adapters.EightMusesDescription import exh.ui.metadata.adapters.EightMusesDescription
import exh.ui.metadata.adapters.HBrowseDescription import exh.ui.metadata.adapters.HBrowseDescription
@ -91,11 +97,15 @@ import exh.ui.metadata.adapters.MangaDexDescription
import exh.ui.metadata.adapters.NHentaiDescription import exh.ui.metadata.adapters.NHentaiDescription
import exh.ui.metadata.adapters.PururinDescription import exh.ui.metadata.adapters.PururinDescription
import exh.ui.metadata.adapters.TsuminoDescription import exh.ui.metadata.adapters.TsuminoDescription
import java.text.DateFormat
import java.util.Date
@Composable @Composable
fun MangaScreen( fun MangaScreen(
state: MangaScreenState.Success, state: MangaScreenState.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int,
dateFormat: DateFormat,
isTabletUi: Boolean, isTabletUi: Boolean,
onBackClicked: () -> Unit, onBackClicked: () -> Unit,
onChapterClicked: (Chapter) -> Unit, onChapterClicked: (Chapter) -> Unit,
@ -144,6 +154,8 @@ fun MangaScreen(
MangaScreenSmallImpl( MangaScreenSmallImpl(
state = state, state = state,
snackbarHostState = snackbarHostState, snackbarHostState = snackbarHostState,
dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat,
onBackClicked = onBackClicked, onBackClicked = onBackClicked,
onChapterClicked = onChapterClicked, onChapterClicked = onChapterClicked,
onDownloadChapter = onDownloadChapter, onDownloadChapter = onDownloadChapter,
@ -183,6 +195,8 @@ fun MangaScreen(
MangaScreenLargeImpl( MangaScreenLargeImpl(
state = state, state = state,
snackbarHostState = snackbarHostState, snackbarHostState = snackbarHostState,
dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat,
onBackClicked = onBackClicked, onBackClicked = onBackClicked,
onChapterClicked = onChapterClicked, onChapterClicked = onChapterClicked,
onDownloadChapter = onDownloadChapter, onDownloadChapter = onDownloadChapter,
@ -225,6 +239,8 @@ fun MangaScreen(
private fun MangaScreenSmallImpl( private fun MangaScreenSmallImpl(
state: MangaScreenState.Success, state: MangaScreenState.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int,
dateFormat: DateFormat,
onBackClicked: () -> Unit, onBackClicked: () -> Unit,
onChapterClicked: (Chapter) -> Unit, onChapterClicked: (Chapter) -> Unit,
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?, onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
@ -488,7 +504,13 @@ private fun MangaScreenSmallImpl(
} }
sharedChapterItems( sharedChapterItems(
manga = state.manga,
chapters = chapters, chapters = chapters,
dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat,
// SY -->
alwaysShowReadingProgress = state.alwaysShowReadingProgress,
// SY <--
onChapterClicked = onChapterClicked, onChapterClicked = onChapterClicked,
onDownloadChapter = onDownloadChapter, onDownloadChapter = onDownloadChapter,
onChapterSelected = onChapterSelected, onChapterSelected = onChapterSelected,
@ -503,6 +525,8 @@ private fun MangaScreenSmallImpl(
fun MangaScreenLargeImpl( fun MangaScreenLargeImpl(
state: MangaScreenState.Success, state: MangaScreenState.Success,
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
dateRelativeTime: Int,
dateFormat: DateFormat,
onBackClicked: () -> Unit, onBackClicked: () -> Unit,
onChapterClicked: (Chapter) -> Unit, onChapterClicked: (Chapter) -> Unit,
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?, onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
@ -741,7 +765,13 @@ fun MangaScreenLargeImpl(
} }
sharedChapterItems( sharedChapterItems(
manga = state.manga,
chapters = chapters, chapters = chapters,
dateRelativeTime = dateRelativeTime,
dateFormat = dateFormat,
// SY -->
alwaysShowReadingProgress = state.alwaysShowReadingProgress,
// SY <--
onChapterClicked = onChapterClicked, onChapterClicked = onChapterClicked,
onDownloadChapter = onDownloadChapter, onDownloadChapter = onDownloadChapter,
onChapterSelected = onChapterSelected, onChapterSelected = onChapterSelected,
@ -797,7 +827,13 @@ private fun SharedMangaBottomActionMenu(
} }
private fun LazyListScope.sharedChapterItems( private fun LazyListScope.sharedChapterItems(
manga: Manga,
chapters: List<ChapterItem>, chapters: List<ChapterItem>,
dateRelativeTime: Int,
dateFormat: DateFormat,
// SY -->
alwaysShowReadingProgress: Boolean,
// SY <--
onChapterClicked: (Chapter) -> Unit, onChapterClicked: (Chapter) -> Unit,
onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?, onDownloadChapter: ((List<ChapterItem>, ChapterDownloadAction) -> Unit)?,
onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit, onChapterSelected: (ChapterItem, Boolean, Boolean, Boolean) -> Unit,
@ -808,10 +844,40 @@ private fun LazyListScope.sharedChapterItems(
contentType = { MangaScreenItem.CHAPTER }, contentType = { MangaScreenItem.CHAPTER },
) { chapterItem -> ) { chapterItem ->
val haptic = LocalHapticFeedback.current val haptic = LocalHapticFeedback.current
val context = LocalContext.current
MangaChapterListItem( MangaChapterListItem(
title = chapterItem.chapterTitleString, title = if (manga.displayMode == Manga.CHAPTER_DISPLAY_NUMBER) {
date = chapterItem.dateUploadString, stringResource(
readProgress = chapterItem.readProgressString, R.string.display_mode_chapter,
chapterDecimalFormat.format(chapterItem.chapter.chapterNumber.toDouble()),
)
} else {
chapterItem.chapter.name
},
date = chapterItem.chapter.dateUpload
.takeIf { it > 0L }
?.let {
// SY -->
if (manga.isEhBasedManga()) {
MetadataUtil.EX_DATE_FORMAT.format(Date(it))
} else {
Date(it).toRelativeString(
context,
dateRelativeTime,
dateFormat,
)
}
// SY <--
},
readProgress = chapterItem.chapter.lastPageRead
.takeIf { /* SY --> */(!chapterItem.chapter.read || alwaysShowReadingProgress)/* SY <-- */ && it > 0L }
?.let {
stringResource(
R.string.chapter_progress,
it + 1,
)
},
scanlator = chapterItem.chapter.scanlator.takeIf { !it.isNullOrBlank() /* SY --> */ && chapterItem.showScanlator /* SY <-- */ }, scanlator = chapterItem.chapter.scanlator.takeIf { !it.isNullOrBlank() /* SY --> */ && chapterItem.showScanlator /* SY <-- */ },
// SY --> // SY -->
sourceName = chapterItem.sourceName, sourceName = chapterItem.sourceName,

View File

@ -45,6 +45,9 @@ fun UpdateScreen(
snackbarHostState: SnackbarHostState, snackbarHostState: SnackbarHostState,
lastUpdated: Long, lastUpdated: Long,
relativeTime: Int, relativeTime: Int,
// SY -->
preserveReadingPosition: Boolean,
// SY <--
onClickCover: (UpdatesItem) -> Unit, onClickCover: (UpdatesItem) -> Unit,
onSelectAll: (Boolean) -> Unit, onSelectAll: (Boolean) -> Unit,
onInvertSelection: () -> Unit, onInvertSelection: () -> Unit,
@ -117,6 +120,9 @@ fun UpdateScreen(
updatesUiItems( updatesUiItems(
uiModels = state.getUiModel(context, relativeTime), uiModels = state.getUiModel(context, relativeTime),
selectionMode = state.selectionMode, selectionMode = state.selectionMode,
// SY -->
preserveReadingPosition = preserveReadingPosition,
// SY <--
onUpdateSelected = onUpdateSelected, onUpdateSelected = onUpdateSelected,
onClickCover = onClickCover, onClickCover = onClickCover,
onClickUpdate = onOpenChapter, onClickUpdate = onOpenChapter,
@ -193,7 +199,7 @@ private fun UpdatesBottomBar(
}.takeIf { selected.fastAny { !it.update.read } }, }.takeIf { selected.fastAny { !it.update.read } },
onMarkAsUnreadClicked = { onMarkAsUnreadClicked = {
onMultiMarkAsReadClicked(selected, false) onMultiMarkAsReadClicked(selected, false)
}.takeIf { selected.fastAny { it.update.read } }, }.takeIf { selected.fastAny { it.update.read || it.update.lastPageRead > 0L } },
onDownloadClicked = { onDownloadClicked = {
onDownloadChapter(selected, ChapterDownloadAction.START) onDownloadChapter(selected, ChapterDownloadAction.START)
}.takeIf { }.takeIf {

View File

@ -38,6 +38,7 @@ import eu.kanade.presentation.components.ChapterDownloadAction
import eu.kanade.presentation.components.ChapterDownloadIndicator import eu.kanade.presentation.components.ChapterDownloadIndicator
import eu.kanade.presentation.components.ListGroupHeader import eu.kanade.presentation.components.ListGroupHeader
import eu.kanade.presentation.components.MangaCover import eu.kanade.presentation.components.MangaCover
import eu.kanade.presentation.manga.components.DotSeparatorText
import eu.kanade.presentation.util.ReadItemAlpha import eu.kanade.presentation.util.ReadItemAlpha
import eu.kanade.presentation.util.padding import eu.kanade.presentation.util.padding
import eu.kanade.presentation.util.selectedBackground import eu.kanade.presentation.util.selectedBackground
@ -80,6 +81,9 @@ fun LazyListScope.updatesLastUpdatedItem(
fun LazyListScope.updatesUiItems( fun LazyListScope.updatesUiItems(
uiModels: List<UpdatesUiModel>, uiModels: List<UpdatesUiModel>,
selectionMode: Boolean, selectionMode: Boolean,
// SY -->
preserveReadingPosition: Boolean,
// SY <--
onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit, onUpdateSelected: (UpdatesItem, Boolean, Boolean, Boolean) -> Unit,
onClickCover: (UpdatesItem) -> Unit, onClickCover: (UpdatesItem) -> Unit,
onClickUpdate: (UpdatesItem) -> Unit, onClickUpdate: (UpdatesItem) -> Unit,
@ -113,6 +117,14 @@ fun LazyListScope.updatesUiItems(
modifier = Modifier.animateItemPlacement(), modifier = Modifier.animateItemPlacement(),
update = updatesItem.update, update = updatesItem.update,
selected = updatesItem.selected, selected = updatesItem.selected,
readProgress = updatesItem.update.lastPageRead
.takeIf { /* SY --> */(!updatesItem.update.read || (preserveReadingPosition && updatesItem.isEhBasedUpdate()))/* SY <-- */ && it > 0L }
?.let {
stringResource(
R.string.chapter_progress,
it + 1,
)
},
onLongClick = { onLongClick = {
onUpdateSelected(updatesItem, !updatesItem.selected, true, true) onUpdateSelected(updatesItem, !updatesItem.selected, true, true)
}, },
@ -139,6 +151,7 @@ fun UpdatesUiItem(
modifier: Modifier, modifier: Modifier,
update: UpdatesWithRelations, update: UpdatesWithRelations,
selected: Boolean, selected: Boolean,
readProgress: String?,
onClick: () -> Unit, onClick: () -> Unit,
onLongClick: () -> Unit, onLongClick: () -> Unit,
onClickCover: (() -> Unit)?, onClickCover: (() -> Unit)?,
@ -203,8 +216,19 @@ fun UpdatesUiItem(
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
onTextLayout = { textHeight = it.size.height }, onTextLayout = { textHeight = it.size.height },
modifier = Modifier.alpha(textAlpha), modifier = Modifier
.weight(weight = 1f, fill = false)
.alpha(textAlpha),
) )
if (readProgress != null) {
DotSeparatorText()
Text(
text = readProgress,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.alpha(ReadItemAlpha),
)
}
} }
} }
ChapterDownloadIndicator( ChapterDownloadIndicator(

View File

@ -140,6 +140,8 @@ class MangaScreen(
MangaScreen( MangaScreen(
state = successState, state = successState,
snackbarHostState = screenModel.snackbarHostState, snackbarHostState = screenModel.snackbarHostState,
dateRelativeTime = screenModel.relativeTime,
dateFormat = screenModel.dateFormat,
isTabletUi = isTabletUi(), isTabletUi = isTabletUi(),
onBackClicked = navigator::pop, onBackClicked = navigator::pop,
onChapterClicked = { openChapter(context, it) }, onChapterClicked = { openChapter(context, it) },

View File

@ -4,9 +4,12 @@ import android.content.Context
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SnackbarResult
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import cafe.adriel.voyager.core.model.StateScreenModel import cafe.adriel.voyager.core.model.StateScreenModel
import cafe.adriel.voyager.core.model.coroutineScope import cafe.adriel.voyager.core.model.coroutineScope
import eu.kanade.core.prefs.CheckboxState import eu.kanade.core.prefs.CheckboxState
import eu.kanade.core.prefs.asState
import eu.kanade.core.prefs.mapAsCheckboxState import eu.kanade.core.prefs.mapAsCheckboxState
import eu.kanade.core.util.addOrRemove import eu.kanade.core.util.addOrRemove
import eu.kanade.data.chapter.NoChaptersException import eu.kanade.data.chapter.NoChaptersException
@ -75,7 +78,6 @@ import eu.kanade.tachiyomi.util.chapter.getChapterSort
import eu.kanade.tachiyomi.util.chapter.getNextUnread import eu.kanade.tachiyomi.util.chapter.getNextUnread
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchNonCancellable import eu.kanade.tachiyomi.util.lang.launchNonCancellable
import eu.kanade.tachiyomi.util.lang.toRelativeString
import eu.kanade.tachiyomi.util.lang.withIOContext import eu.kanade.tachiyomi.util.lang.withIOContext
import eu.kanade.tachiyomi.util.lang.withNonCancellableContext import eu.kanade.tachiyomi.util.lang.withNonCancellableContext
import eu.kanade.tachiyomi.util.lang.withUIContext import eu.kanade.tachiyomi.util.lang.withUIContext
@ -88,7 +90,6 @@ import exh.log.xLogD
import exh.md.utils.FollowStatus import exh.md.utils.FollowStatus
import exh.md.utils.MdUtil import exh.md.utils.MdUtil
import exh.merged.sql.models.MergedMangaReference import exh.merged.sql.models.MergedMangaReference
import exh.metadata.MetadataUtil
import exh.metadata.metadata.base.FlatMetadata import exh.metadata.metadata.base.FlatMetadata
import exh.metadata.metadata.base.RaisedSearchMetadata import exh.metadata.metadata.base.RaisedSearchMetadata
import exh.source.MERGED_SOURCE_ID import exh.source.MERGED_SOURCE_ID
@ -114,10 +115,8 @@ import logcat.LogPriority
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy import uy.kohesive.injekt.injectLazy
import java.text.DateFormat
import java.text.DecimalFormat import java.text.DecimalFormat
import java.text.DecimalFormatSymbols import java.text.DecimalFormatSymbols
import java.util.Date
class MangaInfoScreenModel( class MangaInfoScreenModel(
val context: Context, val context: Context,
@ -179,6 +178,9 @@ class MangaInfoScreenModel(
private val processedChapters: Sequence<ChapterItem>? private val processedChapters: Sequence<ChapterItem>?
get() = successState?.processedChapters get() = successState?.processedChapters
val relativeTime by uiPreferences.relativeTime().asState(coroutineScope)
val dateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get()))
private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list private val selectedPositions: Array<Int> = arrayOf(-1, -1) // first and last selected index in list
private val selectedChapterIds: HashSet<Long> = HashSet() private val selectedChapterIds: HashSet<Long> = HashSet()
@ -213,23 +215,6 @@ class MangaInfoScreenModel(
} }
init { init {
val toChapterItemsParams: List<Chapter>.(manga: Manga /* SY --> */, mergedData: MergedMangaData? /* SY <-- */) -> List<ChapterItem> = { manga, mergedData ->
toChapterItems(
context = context,
manga = manga,
// SY -->
dateRelativeTime = if (manga.isEhBasedManga()) 0 else uiPreferences.relativeTime().get(),
dateFormat = if (manga.isEhBasedManga()) {
MetadataUtil.EX_DATE_FORMAT
} else {
UiPreferences.dateFormat(uiPreferences.dateFormat().get())
},
mergedData = mergedData,
alwaysShowReadingProgress = readerPreferences.preserveReadingPosition().get() && manga.isEhBasedManga(),
// SY <--
)
}
coroutineScope.launchIO { coroutineScope.launchIO {
getMangaAndChapters.subscribe(mangaId) getMangaAndChapters.subscribe(mangaId)
.distinctUntilChanged() .distinctUntilChanged()
@ -295,7 +280,7 @@ class MangaInfoScreenModel(
.combine(downloadCache.changes) { state, _ -> state } .combine(downloadCache.changes) { state, _ -> state }
// SY <-- // SY <--
.collectLatest { (manga, chapters /* SY --> */, flatMetadata, mergedData /* SY <-- */) -> .collectLatest { (manga, chapters /* SY --> */, flatMetadata, mergedData /* SY <-- */) ->
val chapterItems = chapters.toChapterItemsParams(manga /* SY --> */, mergedData /* SY <-- */) val chapterItems = chapters.toChapterItems(manga /* SY --> */, mergedData /* SY <-- */)
updateSuccessState { updateSuccessState {
it.copy( it.copy(
manga = manga, manga = manga,
@ -316,7 +301,7 @@ class MangaInfoScreenModel(
val manga = getMangaAndChapters.awaitManga(mangaId) val manga = getMangaAndChapters.awaitManga(mangaId)
// SY --> // SY -->
val chapters = (if (manga.source == MERGED_SOURCE_ID) getMergedChapterByMangaId.await(mangaId) else getMangaAndChapters.awaitChapters(mangaId)) val chapters = (if (manga.source == MERGED_SOURCE_ID) getMergedChapterByMangaId.await(mangaId) else getMangaAndChapters.awaitChapters(mangaId))
.toChapterItemsParams(manga, null) .toChapterItems(manga, null)
val mergedData = getMergedReferencesById.await(mangaId).takeIf { it.isNotEmpty() }?.let { references -> val mergedData = getMergedReferencesById.await(mangaId).takeIf { it.isNotEmpty() }?.let { references ->
MergedMangaData( MergedMangaData(
references, references,
@ -358,6 +343,7 @@ class MangaInfoScreenModel(
PagePreviewState.Unused PagePreviewState.Unused
}, },
scanlators = getChapterScanlators(manga, chapters.map { it.chapter }), scanlators = getChapterScanlators(manga, chapters.map { it.chapter }),
alwaysShowReadingProgress = readerPreferences.preserveReadingPosition().get() && manga.isEhBasedManga(),
// SY <-- // SY <--
) )
} }
@ -906,12 +892,8 @@ class MangaInfoScreenModel(
} }
private fun List<Chapter>.toChapterItems( private fun List<Chapter>.toChapterItems(
context: Context,
manga: Manga, manga: Manga,
dateRelativeTime: Int,
dateFormat: DateFormat,
mergedData: MergedMangaData?, mergedData: MergedMangaData?,
alwaysShowReadingProgress: Boolean,
): List<ChapterItem> { ): List<ChapterItem> {
val isLocal = manga.isLocal() val isLocal = manga.isLocal()
// SY --> // SY -->
@ -951,29 +933,6 @@ class MangaInfoScreenModel(
chapter = chapter, chapter = chapter,
downloadState = downloadState, downloadState = downloadState,
downloadProgress = activeDownload?.progress ?: 0, downloadProgress = activeDownload?.progress ?: 0,
chapterTitleString = if (manga.displayMode == Manga.CHAPTER_DISPLAY_NUMBER) {
context.getString(
R.string.display_mode_chapter,
chapterDecimalFormat.format(chapter.chapterNumber.toDouble()),
)
} else {
chapter.name
},
dateUploadString = chapter.dateUpload
.takeIf { it > 0 }
?.let {
Date(it).toRelativeString(
context,
dateRelativeTime,
dateFormat,
)
},
readProgressString = chapter.lastPageRead.takeIf { /* SY --> */(!chapter.read || alwaysShowReadingProgress)/* SY <-- */ && it > 0 }?.let {
context.getString(
R.string.chapter_progress,
it + 1,
)
},
selected = chapter.id in selectedChapterIds, selected = chapter.id in selectedChapterIds,
// SY --> // SY -->
sourceName = source?.getNameForMangaInfo(null, enabledLanguages = enabledLanguages), sourceName = source?.getNameForMangaInfo(null, enabledLanguages = enabledLanguages),
@ -1601,6 +1560,7 @@ sealed class MangaScreenState {
val showMergeWithAnother: Boolean, val showMergeWithAnother: Boolean,
val pagePreviewsState: PagePreviewState, val pagePreviewsState: PagePreviewState,
val scanlators: List<String>, val scanlators: List<String>,
val alwaysShowReadingProgress: Boolean,
// SY <-- // SY <--
) : MangaScreenState() { ) : MangaScreenState() {
@ -1659,11 +1619,6 @@ data class ChapterItem(
val chapter: Chapter, val chapter: Chapter,
val downloadState: Download.State, val downloadState: Download.State,
val downloadProgress: Int, val downloadProgress: Int,
val chapterTitleString: String,
val dateUploadString: String?,
val readProgressString: String?,
val selected: Boolean = false, val selected: Boolean = false,
// SY --> // SY -->

View File

@ -28,11 +28,14 @@ import eu.kanade.tachiyomi.data.download.DownloadService
import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.download.model.Download
import eu.kanade.tachiyomi.data.library.LibraryUpdateService import eu.kanade.tachiyomi.data.library.LibraryUpdateService
import eu.kanade.tachiyomi.source.SourceManager import eu.kanade.tachiyomi.source.SourceManager
import eu.kanade.tachiyomi.ui.reader.setting.ReaderPreferences
import eu.kanade.tachiyomi.util.lang.launchIO import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchNonCancellable import eu.kanade.tachiyomi.util.lang.launchNonCancellable
import eu.kanade.tachiyomi.util.lang.toDateKey import eu.kanade.tachiyomi.util.lang.toDateKey
import eu.kanade.tachiyomi.util.lang.toRelativeString import eu.kanade.tachiyomi.util.lang.toRelativeString
import eu.kanade.tachiyomi.util.system.logcat import eu.kanade.tachiyomi.util.system.logcat
import exh.source.EH_SOURCE_ID
import exh.source.EXH_SOURCE_ID
import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.catch
@ -46,7 +49,6 @@ import kotlinx.coroutines.launch
import logcat.LogPriority import logcat.LogPriority
import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get import uy.kohesive.injekt.api.get
import java.text.DateFormat
import java.util.Calendar import java.util.Calendar
import java.util.Date import java.util.Date
@ -62,15 +64,20 @@ class UpdatesScreenModel(
private val libraryPreferences: LibraryPreferences = Injekt.get(), private val libraryPreferences: LibraryPreferences = Injekt.get(),
val snackbarHostState: SnackbarHostState = SnackbarHostState(), val snackbarHostState: SnackbarHostState = SnackbarHostState(),
uiPreferences: UiPreferences = Injekt.get(), uiPreferences: UiPreferences = Injekt.get(),
// SY -->
readerPreferences: ReaderPreferences = Injekt.get(),
// SY <--
) : StateScreenModel<UpdatesState>(UpdatesState()) { ) : StateScreenModel<UpdatesState>(UpdatesState()) {
private val _events: Channel<Event> = Channel(Int.MAX_VALUE) private val _events: Channel<Event> = Channel(Int.MAX_VALUE)
val events: Flow<Event> = _events.receiveAsFlow() val events: Flow<Event> = _events.receiveAsFlow()
val lastUpdated by libraryPreferences.libraryUpdateLastTimestamp().asState(coroutineScope) val lastUpdated by libraryPreferences.libraryUpdateLastTimestamp().asState(coroutineScope)
val relativeTime by uiPreferences.relativeTime().asState(coroutineScope)
val relativeTime: Int by uiPreferences.relativeTime().asState(coroutineScope) // SY -->
val dateFormat: DateFormat by mutableStateOf(UiPreferences.dateFormat(uiPreferences.dateFormat().get())) val preserveReadingPosition by readerPreferences.preserveReadingPosition().asState(coroutineScope)
// SY <--
// First and last selected index in list // First and last selected index in list
private val selectedPositions: Array<Int> = arrayOf(-1, -1) private val selectedPositions: Array<Int> = arrayOf(-1, -1)
@ -110,15 +117,15 @@ class UpdatesScreenModel(
} }
private fun List<UpdatesWithRelations>.toUpdateItems(): List<UpdatesItem> { private fun List<UpdatesWithRelations>.toUpdateItems(): List<UpdatesItem> {
return this.map { return this.map { update ->
val activeDownload = downloadManager.getQueuedDownloadOrNull(it.chapterId) val activeDownload = downloadManager.getQueuedDownloadOrNull(update.chapterId)
val downloaded = downloadManager.isChapterDownloaded( val downloaded = downloadManager.isChapterDownloaded(
it.chapterName, update.chapterName,
it.scanlator, update.scanlator,
// SY --> // SY -->
it.ogMangaTitle, update.ogMangaTitle,
// SY <-- // SY <--
it.sourceId, update.sourceId,
) )
val downloadState = when { val downloadState = when {
activeDownload != null -> activeDownload.status activeDownload != null -> activeDownload.status
@ -126,10 +133,10 @@ class UpdatesScreenModel(
else -> Download.State.NOT_DOWNLOADED else -> Download.State.NOT_DOWNLOADED
} }
UpdatesItem( UpdatesItem(
update = it, update = update,
downloadStateProvider = { downloadState }, downloadStateProvider = { downloadState },
downloadProgressProvider = { activeDownload?.progress ?: 0 }, downloadProgressProvider = { activeDownload?.progress ?: 0 },
selected = it.chapterId in selectedChapterIds, selected = update.chapterId in selectedChapterIds,
) )
} }
} }
@ -392,7 +399,8 @@ data class UpdatesState(
val selectionMode = selected.isNotEmpty() val selectionMode = selected.isNotEmpty()
fun getUiModel(context: Context, relativeTime: Int): List<UpdatesUiModel> { fun getUiModel(context: Context, relativeTime: Int): List<UpdatesUiModel> {
val dateFormat = UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()) val dateFormat by mutableStateOf(UiPreferences.dateFormat(Injekt.get<UiPreferences>().dateFormat().get()))
return items return items
.map { UpdatesUiModel.Item(it) } .map { UpdatesUiModel.Item(it) }
.insertSeparators { before, after -> .insertSeparators { before, after ->
@ -420,4 +428,10 @@ data class UpdatesItem(
val downloadStateProvider: () -> Download.State, val downloadStateProvider: () -> Download.State,
val downloadProgressProvider: () -> Int, val downloadProgressProvider: () -> Int,
val selected: Boolean = false, val selected: Boolean = false,
) ) {
// SY -->
fun isEhBasedUpdate(): Boolean {
return update.sourceId == EH_SOURCE_ID || update.sourceId == EXH_SOURCE_ID
}
// SY <--
}

View File

@ -74,6 +74,9 @@ object UpdatesTab : Tab {
snackbarHostState = screenModel.snackbarHostState, snackbarHostState = screenModel.snackbarHostState,
lastUpdated = screenModel.lastUpdated, lastUpdated = screenModel.lastUpdated,
relativeTime = screenModel.relativeTime, relativeTime = screenModel.relativeTime,
// SY -->
preserveReadingPosition = screenModel.preserveReadingPosition,
// SY <--
onClickCover = { item -> navigator.push(MangaScreen(item.update.mangaId)) }, onClickCover = { item -> navigator.push(MangaScreen(item.update.mangaId)) },
onSelectAll = screenModel::toggleAllSelection, onSelectAll = screenModel::toggleAllSelection,
onInvertSelection = screenModel::invertSelection, onInvertSelection = screenModel::invertSelection,

View File

@ -0,0 +1,23 @@
DROP VIEW IF EXISTS updatesView;
CREATE VIEW updatesView AS
SELECT
mangas._id AS mangaId,
mangas.title AS mangaTitle,
chapters._id AS chapterId,
chapters.name AS chapterName,
chapters.scanlator,
chapters.read,
chapters.bookmark,
chapters.last_page_read,
mangas.source,
mangas.favorite,
mangas.thumbnail_url AS thumbnailUrl,
mangas.cover_last_modified AS coverLastModified,
chapters.date_upload AS dateUpload,
chapters.date_fetch AS datefetch
FROM mangas JOIN chapters
ON mangas._id = chapters.manga_id
WHERE favorite = 1
AND date_fetch > date_added
ORDER BY date_fetch DESC;

View File

@ -7,6 +7,7 @@ SELECT
chapters.scanlator, chapters.scanlator,
chapters.read, chapters.read,
chapters.bookmark, chapters.bookmark,
chapters.last_page_read,
mangas.source, mangas.source,
mangas.favorite, mangas.favorite,
mangas.thumbnail_url AS thumbnailUrl, mangas.thumbnail_url AS thumbnailUrl,