Refactor reader progress/history logic

(cherry picked from commit 226272f686ccf4dea1cc4e81b0c305749d888231)

# Conflicts:
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt
#	app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
This commit is contained in:
arkon 2023-07-08 18:05:00 -04:00 committed by Jobobby04
parent e1fc81cc66
commit 9bacd14f4d
2 changed files with 81 additions and 87 deletions

View File

@ -296,13 +296,18 @@ class ReaderActivity : BaseActivity() {
readingModeToast?.cancel() readingModeToast?.cancel()
} }
override fun onPause() {
viewModel.flushReadTimer()
super.onPause()
}
/** /**
* Set menu visibility again on activity resume to apply immersive mode again if needed. * Set menu visibility again on activity resume to apply immersive mode again if needed.
* Helps with rotations. * Helps with rotations.
*/ */
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
viewModel.setReadStartTime() viewModel.restartReadTimer()
setMenuVisibility(viewModel.state.value.menuVisible, animate = false) setMenuVisibility(viewModel.state.value.menuVisible, animate = false)
} }
@ -1005,7 +1010,7 @@ class ReaderActivity : BaseActivity() {
* Sets the visibility of the menu according to [visible] and with an optional parameter to * Sets the visibility of the menu according to [visible] and with an optional parameter to
* [animate] the views. * [animate] the views.
*/ */
fun setMenuVisibility(visible: Boolean, animate: Boolean = true) { private fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
viewModel.showMenus(visible) viewModel.showMenus(visible)
if (visible) { if (visible) {
windowInsetsController.show(WindowInsetsCompat.Type.systemBars()) windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
@ -1301,13 +1306,13 @@ class ReaderActivity : BaseActivity() {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean = false) { fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean = false) {
// SY --> // SY -->
val currentPage = if (hasExtraPage) { val currentPageText = if (hasExtraPage) {
val invertDoublePage = (viewModel.state.value.viewer as? PagerViewer)?.config?.invertDoublePages ?: false val invertDoublePage = (viewModel.state.value.viewer as? PagerViewer)?.config?.invertDoublePages ?: false
if (resources.isLTR xor invertDoublePage) "${page.number}-${page.number + 1}" else "${page.number + 1}-${page.number}" if (resources.isLTR xor invertDoublePage) "${page.number}-${page.number + 1}" else "${page.number + 1}-${page.number}"
} else { } else {
"${page.number}" "${page.number}"
} }
viewModel.onPageSelected(page, hasExtraPage, currentPage) viewModel.onPageSelected(page, currentPageText)
// SY <-- // SY <--
} }
@ -1326,7 +1331,7 @@ class ReaderActivity : BaseActivity() {
* the viewer is reaching the beginning or end of a chapter or the transition page is active. * the viewer is reaching the beginning or end of a chapter or the transition page is active.
*/ */
fun requestPreloadChapter(chapter: ReaderChapter) { fun requestPreloadChapter(chapter: ReaderChapter) {
lifecycleScope.launchIO { viewModel.preloadChapter(chapter) } lifecycleScope.launchIO { viewModel.preload(chapter) }
} }
/** /**
@ -1421,7 +1426,7 @@ class ReaderActivity : BaseActivity() {
/** /**
* Updates viewer inset depending on fullscreen reader preferences. * Updates viewer inset depending on fullscreen reader preferences.
*/ */
fun updateViewerInset(fullscreen: Boolean) { private fun updateViewerInset(fullscreen: Boolean) {
viewModel.state.value.viewer?.getView()?.applyInsetter { viewModel.state.value.viewer?.getView()?.applyInsetter {
if (!fullscreen) { if (!fullscreen) {
type(navigationBars = true, statusBars = true) { type(navigationBars = true, statusBars = true) {

View File

@ -75,7 +75,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import logcat.LogPriority import logcat.LogPriority
import tachiyomi.core.util.lang.launchIO import tachiyomi.core.util.lang.launchIO
@ -410,12 +409,15 @@ class ReaderViewModel(
* Called when the user changed to the given [chapter] when changing pages from the viewer. * Called when the user changed to the given [chapter] when changing pages from the viewer.
* It's used only to set this chapter as active. * It's used only to set this chapter as active.
*/ */
private suspend fun loadNewChapter(chapter: ReaderChapter) { private fun loadNewChapter(chapter: ReaderChapter) {
val loader = loader ?: return val loader = loader ?: return
logcat { "Loading ${chapter.chapter.url}" } viewModelScope.launchIO {
logcat { "Loading ${chapter.chapter.url}" }
flushReadTimer()
restartReadTimer()
withIOContext {
try { try {
loadChapter(loader, chapter) loadChapter(loader, chapter)
} catch (e: Throwable) { } catch (e: Throwable) {
@ -459,7 +461,7 @@ class ReaderViewModel(
* Called when the viewers decide it's a good time to preload a [chapter] and improve the UX so * Called when the viewers decide it's a good time to preload a [chapter] and improve the UX so
* that the user doesn't have to wait too long to continue reading. * that the user doesn't have to wait too long to continue reading.
*/ */
private suspend fun preload(chapter: ReaderChapter) { suspend fun preload(chapter: ReaderChapter) {
if (chapter.state is ReaderChapter.State.Loaded || chapter.state == ReaderChapter.State.Loading) { if (chapter.state is ReaderChapter.State.Loaded || chapter.state == ReaderChapter.State.Loading) {
return return
} }
@ -498,9 +500,7 @@ class ReaderViewModel(
fun onViewerLoaded(viewer: Viewer?) { fun onViewerLoaded(viewer: Viewer?) {
mutableState.update { mutableState.update {
it.copy( it.copy(viewer = viewer)
viewer = viewer,
)
} }
} }
@ -509,53 +509,29 @@ class ReaderViewModel(
* read, update tracking services, enqueue downloaded chapter deletion, and updating the active chapter if this * read, update tracking services, enqueue downloaded chapter deletion, and updating the active chapter if this
* [page]'s chapter is different from the currently active. * [page]'s chapter is different from the currently active.
*/ */
fun onPageSelected(page: ReaderPage, hasExtraPage: Boolean, currentPage: String) { fun onPageSelected(page: ReaderPage, currentPageText: String) {
// InsertPage and StencilPage doesn't change page progress // InsertPage and StencilPage doesn't change page progress
if (page is InsertPage || page is StencilPage) { if (page is InsertPage || page is StencilPage) {
return return
} }
val currentChapters = state.value.viewerChapters ?: return // SY -->
val pages = page.chapter.pages ?: return mutableState.update { it.copy(currentPageText = currentPageText) }
// SY <--
val selectedChapter = page.chapter val selectedChapter = page.chapter
val pages = selectedChapter.pages ?: return
// Save last page read and mark as read if needed // Save last page read and mark as read if needed
saveReadingProgress() viewModelScope.launchNonCancellable {
mutableState.update { updateChapterProgress(page.index)
it.copy(
currentPage = page.number,
// SY -->
currentPageText = currentPage,
// SY <--
)
}
selectedChapter.chapter.last_page_read = page.index
if (
(selectedChapter.pages?.lastIndex == page.index && !incognitoMode) ||
(hasExtraPage && selectedChapter.pages?.lastIndex?.minus(1) == page.index && !incognitoMode)
) {
selectedChapter.chapter.read = true
// SY -->
if (manga?.isEhBasedManga() == true) {
viewModelScope.launchNonCancellable {
chapterList
.filter { it.chapter.source_order > selectedChapter.chapter.source_order }
.onEach {
it.chapter.read = true
saveChapterProgress(it)
}
}
}
// SY <--
updateTrackChapterRead(selectedChapter)
deleteChapterIfNeeded(selectedChapter)
} }
if (selectedChapter != currentChapters.currChapter) { if (selectedChapter != getCurrentChapter()) {
logcat { "Setting ${selectedChapter.chapter.url} as active" } logcat { "Setting ${selectedChapter.chapter.url} as active" }
setReadStartTime() loadNewChapter(selectedChapter)
viewModelScope.launch { loadNewChapter(selectedChapter) }
} }
val inDownloadRange = page.number.toDouble() / pages.size > 0.25 val inDownloadRange = page.number.toDouble() / pages.size > 0.25
if (inDownloadRange) { if (inDownloadRange) {
downloadNextChapters() downloadNextChapters()
@ -627,45 +603,69 @@ class ReaderViewModel(
} }
/** /**
* Called when reader chapter is changed in reader or when activity is paused. * Saves the chapter progress (last read page and whether it's read)
* if incognito mode isn't on.
*/ */
private fun saveReadingProgress() { private suspend fun updateChapterProgress(pageIndex: Int) {
getCurrentChapter()?.let { val readerChapter = getCurrentChapter() ?: return
viewModelScope.launchNonCancellable {
saveChapterProgress(it) mutableState.update {
saveChapterHistory(it) it.copy(currentPage = pageIndex + 1)
}
if (!incognitoMode) {
readerChapter.requestedPage = pageIndex
readerChapter.chapter.last_page_read = pageIndex
updateChapter.await(
ChapterUpdate(
id = readerChapter.chapter.id!!,
read = readerChapter.chapter.read,
bookmark = readerChapter.chapter.bookmark,
lastPageRead = readerChapter.chapter.last_page_read.toLong(),
),
)
if (readerChapter.pages?.lastIndex == pageIndex) {
readerChapter.chapter.read = true
// SY -->
if (manga?.isEhBasedManga() == true) {
viewModelScope.launchNonCancellable {
val chapterUpdates = chapterList
.filter { it.chapter.source_order > readerChapter.chapter.source_order }
.map { chapter ->
ChapterUpdate(
id = chapter.chapter.id!!,
read = true,
)
}
updateChapter.awaitAll(chapterUpdates)
}
}
// SY <--
updateTrackChapterRead(readerChapter)
deleteChapterIfNeeded(readerChapter)
} }
} }
} }
/** fun restartReadTimer() {
* Saves this [readerChapter] progress (last read page and whether it's read) chapterReadStartTime = Date().time
* if incognito mode isn't on. }
*/
private suspend fun saveChapterProgress(readerChapter: ReaderChapter) {
// SY -->
readerChapter.requestedPage = readerChapter.chapter.last_page_read
// SY <--
if (incognitoMode) return
val chapter = readerChapter.chapter fun flushReadTimer() {
readerChapter.requestedPage = chapter.last_page_read viewModelScope.launchNonCancellable {
updateChapter.await( updateHistory()
ChapterUpdate( }
id = chapter.id!!,
read = chapter.read,
bookmark = chapter.bookmark,
lastPageRead = chapter.last_page_read.toLong(),
),
)
} }
/** /**
* Saves this [readerChapter] last read history if incognito mode isn't on. * Saves the chapter last read history if incognito mode isn't on.
*/ */
private suspend fun saveChapterHistory(readerChapter: ReaderChapter) { private suspend fun updateHistory() {
if (incognitoMode) return if (incognitoMode) return
val readerChapter = getCurrentChapter() ?: return
val chapterId = readerChapter.chapter.id!! val chapterId = readerChapter.chapter.id!!
val endTime = Date() val endTime = Date()
val sessionReadDuration = chapterReadStartTime?.let { endTime.time - it } ?: 0 val sessionReadDuration = chapterReadStartTime?.let { endTime.time - it } ?: 0
@ -674,17 +674,6 @@ class ReaderViewModel(
chapterReadStartTime = null chapterReadStartTime = null
} }
fun setReadStartTime() {
chapterReadStartTime = Date().time
}
/**
* Called from the activity to preload the given [chapter].
*/
suspend fun preloadChapter(chapter: ReaderChapter) {
preload(chapter)
}
/** /**
* Called from the activity to load and set the next chapter as active. * Called from the activity to load and set the next chapter as active.
*/ */