Refactor chapter tracking logic
Could probably call this if we ever make it update tracking on manually marking chapters as read. (cherry picked from commit efabe801be56476bf9ee536747f39ab8d486ca12) # Conflicts: # app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt
This commit is contained in:
parent
16944a6857
commit
16edec18f2
@ -16,6 +16,7 @@ import eu.kanade.domain.source.interactor.SetMigrateSorting
|
|||||||
import eu.kanade.domain.source.interactor.ToggleLanguage
|
import eu.kanade.domain.source.interactor.ToggleLanguage
|
||||||
import eu.kanade.domain.source.interactor.ToggleSource
|
import eu.kanade.domain.source.interactor.ToggleSource
|
||||||
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
import eu.kanade.domain.source.interactor.ToggleSourcePin
|
||||||
|
import eu.kanade.domain.track.interactor.TrackChapter
|
||||||
import tachiyomi.data.category.CategoryRepositoryImpl
|
import tachiyomi.data.category.CategoryRepositoryImpl
|
||||||
import tachiyomi.data.chapter.ChapterRepositoryImpl
|
import tachiyomi.data.chapter.ChapterRepositoryImpl
|
||||||
import tachiyomi.data.history.HistoryRepositoryImpl
|
import tachiyomi.data.history.HistoryRepositoryImpl
|
||||||
@ -109,6 +110,7 @@ class DomainModule : InjektModule {
|
|||||||
addFactory { GetApplicationRelease(get(), get()) }
|
addFactory { GetApplicationRelease(get(), get()) }
|
||||||
|
|
||||||
addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) }
|
addSingletonFactory<TrackRepository> { TrackRepositoryImpl(get()) }
|
||||||
|
addFactory { TrackChapter(get(), get(), get(), get()) }
|
||||||
addFactory { DeleteTrack(get()) }
|
addFactory { DeleteTrack(get()) }
|
||||||
addFactory { GetTracksPerManga(get(), get()) }
|
addFactory { GetTracksPerManga(get(), get()) }
|
||||||
addFactory { GetTracks(get()) }
|
addFactory { GetTracks(get()) }
|
||||||
|
@ -0,0 +1,57 @@
|
|||||||
|
package eu.kanade.domain.track.interactor
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import eu.kanade.domain.track.model.toDbTrack
|
||||||
|
import eu.kanade.domain.track.service.DelayedTrackingUpdateJob
|
||||||
|
import eu.kanade.domain.track.store.DelayedTrackingStore
|
||||||
|
import eu.kanade.tachiyomi.data.track.TrackManager
|
||||||
|
import exh.md.utils.FollowStatus
|
||||||
|
import kotlinx.coroutines.async
|
||||||
|
import kotlinx.coroutines.awaitAll
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
|
import logcat.LogPriority
|
||||||
|
import tachiyomi.core.util.lang.launchNonCancellable
|
||||||
|
import tachiyomi.core.util.system.logcat
|
||||||
|
import tachiyomi.domain.track.interactor.GetTracks
|
||||||
|
import tachiyomi.domain.track.interactor.InsertTrack
|
||||||
|
|
||||||
|
class TrackChapter(
|
||||||
|
private val getTracks: GetTracks,
|
||||||
|
private val trackManager: TrackManager,
|
||||||
|
private val insertTrack: InsertTrack,
|
||||||
|
private val delayedTrackingStore: DelayedTrackingStore,
|
||||||
|
) {
|
||||||
|
|
||||||
|
suspend fun await(context: Context, mangaId: Long, chapterNumber: Double) = coroutineScope {
|
||||||
|
launchNonCancellable {
|
||||||
|
val tracks = getTracks.await(mangaId)
|
||||||
|
|
||||||
|
if (tracks.isEmpty()) return@launchNonCancellable
|
||||||
|
|
||||||
|
tracks.mapNotNull { track ->
|
||||||
|
val service = trackManager.getService(track.syncId)
|
||||||
|
if (service != null && service.isLogged && chapterNumber > track.lastChapterRead /* SY --> */ && ((service.id == TrackManager.MDLIST && track.status != FollowStatus.UNFOLLOWED.int.toLong()) || service.id != TrackManager.MDLIST)/* SY <-- */) {
|
||||||
|
val updatedTrack = track.copy(lastChapterRead = chapterNumber)
|
||||||
|
|
||||||
|
async {
|
||||||
|
runCatching {
|
||||||
|
try {
|
||||||
|
service.update(updatedTrack.toDbTrack(), true)
|
||||||
|
insertTrack.await(updatedTrack)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
delayedTrackingStore.addItem(updatedTrack)
|
||||||
|
DelayedTrackingUpdateJob.setupTask(context)
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.awaitAll()
|
||||||
|
.mapNotNull { it.exceptionOrNull() }
|
||||||
|
.forEach { logcat(LogPriority.INFO, it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -12,10 +12,8 @@ import eu.kanade.domain.chapter.model.toDbChapter
|
|||||||
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
import eu.kanade.domain.manga.interactor.SetMangaViewerFlags
|
||||||
import eu.kanade.domain.manga.model.orientationType
|
import eu.kanade.domain.manga.model.orientationType
|
||||||
import eu.kanade.domain.manga.model.readingModeType
|
import eu.kanade.domain.manga.model.readingModeType
|
||||||
import eu.kanade.domain.track.model.toDbTrack
|
import eu.kanade.domain.track.interactor.TrackChapter
|
||||||
import eu.kanade.domain.track.service.DelayedTrackingUpdateJob
|
|
||||||
import eu.kanade.domain.track.service.TrackPreferences
|
import eu.kanade.domain.track.service.TrackPreferences
|
||||||
import eu.kanade.domain.track.store.DelayedTrackingStore
|
|
||||||
import eu.kanade.domain.ui.UiPreferences
|
import eu.kanade.domain.ui.UiPreferences
|
||||||
import eu.kanade.tachiyomi.data.database.models.toDomainChapter
|
import eu.kanade.tachiyomi.data.database.models.toDomainChapter
|
||||||
import eu.kanade.tachiyomi.data.download.DownloadManager
|
import eu.kanade.tachiyomi.data.download.DownloadManager
|
||||||
@ -24,7 +22,6 @@ import eu.kanade.tachiyomi.data.download.model.Download
|
|||||||
import eu.kanade.tachiyomi.data.saver.Image
|
import eu.kanade.tachiyomi.data.saver.Image
|
||||||
import eu.kanade.tachiyomi.data.saver.ImageSaver
|
import eu.kanade.tachiyomi.data.saver.ImageSaver
|
||||||
import eu.kanade.tachiyomi.data.saver.Location
|
import eu.kanade.tachiyomi.data.saver.Location
|
||||||
import eu.kanade.tachiyomi.data.track.TrackManager
|
|
||||||
import eu.kanade.tachiyomi.source.model.Page
|
import eu.kanade.tachiyomi.source.model.Page
|
||||||
import eu.kanade.tachiyomi.source.online.HttpSource
|
import eu.kanade.tachiyomi.source.online.HttpSource
|
||||||
import eu.kanade.tachiyomi.source.online.MetadataSource
|
import eu.kanade.tachiyomi.source.online.MetadataSource
|
||||||
@ -51,7 +48,6 @@ import eu.kanade.tachiyomi.util.lang.takeBytes
|
|||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
import eu.kanade.tachiyomi.util.storage.DiskUtil
|
||||||
import eu.kanade.tachiyomi.util.storage.DiskUtil.MAX_FILE_NAME_BYTES
|
import eu.kanade.tachiyomi.util.storage.DiskUtil.MAX_FILE_NAME_BYTES
|
||||||
import eu.kanade.tachiyomi.util.storage.cacheImageDir
|
import eu.kanade.tachiyomi.util.storage.cacheImageDir
|
||||||
import eu.kanade.tachiyomi.util.system.isOnline
|
|
||||||
import exh.md.utils.FollowStatus
|
import exh.md.utils.FollowStatus
|
||||||
import exh.md.utils.MdUtil
|
import exh.md.utils.MdUtil
|
||||||
import exh.metadata.metadata.RaisedSearchMetadata
|
import exh.metadata.metadata.RaisedSearchMetadata
|
||||||
@ -62,8 +58,6 @@ import exh.util.defaultReaderType
|
|||||||
import exh.util.mangaType
|
import exh.util.mangaType
|
||||||
import kotlinx.coroutines.CancellationException
|
import kotlinx.coroutines.CancellationException
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
|
||||||
import kotlinx.coroutines.awaitAll
|
|
||||||
import kotlinx.coroutines.channels.Channel
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
@ -100,8 +94,6 @@ import tachiyomi.domain.manga.interactor.GetMergedMangaById
|
|||||||
import tachiyomi.domain.manga.interactor.GetMergedReferencesById
|
import tachiyomi.domain.manga.interactor.GetMergedReferencesById
|
||||||
import tachiyomi.domain.manga.model.Manga
|
import tachiyomi.domain.manga.model.Manga
|
||||||
import tachiyomi.domain.source.service.SourceManager
|
import tachiyomi.domain.source.service.SourceManager
|
||||||
import tachiyomi.domain.track.interactor.GetTracks
|
|
||||||
import tachiyomi.domain.track.interactor.InsertTrack
|
|
||||||
import tachiyomi.source.local.isLocal
|
import tachiyomi.source.local.isLocal
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
@ -123,12 +115,10 @@ class ReaderViewModel(
|
|||||||
private val basePreferences: BasePreferences = Injekt.get(),
|
private val basePreferences: BasePreferences = Injekt.get(),
|
||||||
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
private val downloadPreferences: DownloadPreferences = Injekt.get(),
|
||||||
private val trackPreferences: TrackPreferences = Injekt.get(),
|
private val trackPreferences: TrackPreferences = Injekt.get(),
|
||||||
private val delayedTrackingStore: DelayedTrackingStore = Injekt.get(),
|
private val trackChapter: TrackChapter = Injekt.get(),
|
||||||
private val getManga: GetManga = Injekt.get(),
|
private val getManga: GetManga = Injekt.get(),
|
||||||
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
|
||||||
private val getNextChapters: GetNextChapters = Injekt.get(),
|
private val getNextChapters: GetNextChapters = Injekt.get(),
|
||||||
private val getTracks: GetTracks = Injekt.get(),
|
|
||||||
private val insertTrack: InsertTrack = Injekt.get(),
|
|
||||||
private val upsertHistory: UpsertHistory = Injekt.get(),
|
private val upsertHistory: UpsertHistory = Injekt.get(),
|
||||||
private val updateChapter: UpdateChapter = Injekt.get(),
|
private val updateChapter: UpdateChapter = Injekt.get(),
|
||||||
private val setMangaViewerFlags: SetMangaViewerFlags = Injekt.get(),
|
private val setMangaViewerFlags: SetMangaViewerFlags = Injekt.get(),
|
||||||
@ -254,12 +244,6 @@ class ReaderViewModel(
|
|||||||
.map(::ReaderChapter)
|
.map(::ReaderChapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
private var hasTrackers: Boolean = false
|
|
||||||
private val checkTrackers: (Manga) -> Unit = { manga ->
|
|
||||||
val tracks = runBlocking { getTracks.await(manga.id) }
|
|
||||||
hasTrackers = tracks.isNotEmpty()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val incognitoMode = preferences.incognitoMode().get()
|
private val incognitoMode = preferences.incognitoMode().get()
|
||||||
private val downloadAheadAmount = downloadPreferences.autoDownloadWhileReading().get()
|
private val downloadAheadAmount = downloadPreferences.autoDownloadWhileReading().get()
|
||||||
|
|
||||||
@ -329,8 +313,6 @@ class ReaderViewModel(
|
|||||||
mutableState.update { it.copy(manga = manga /* SY --> */, meta = metadata, mergedManga = mergedManga/* SY <-- */) }
|
mutableState.update { it.copy(manga = manga /* SY --> */, meta = metadata, mergedManga = mergedManga/* SY <-- */) }
|
||||||
if (chapterId == -1L) chapterId = initialChapterId
|
if (chapterId == -1L) chapterId = initialChapterId
|
||||||
|
|
||||||
checkTrackers(manga)
|
|
||||||
|
|
||||||
val context = Injekt.get<Application>()
|
val context = Injekt.get<Application>()
|
||||||
// val source = sourceManager.getOrStub(manga.source)
|
// val source = sourceManager.getOrStub(manga.source)
|
||||||
loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source, /* SY --> */sourceManager, mergedReferences, mergedManga/* SY <-- */)
|
loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source, /* SY --> */sourceManager, mergedReferences, mergedManga/* SY <-- */)
|
||||||
@ -524,7 +506,7 @@ class ReaderViewModel(
|
|||||||
|
|
||||||
// Save last page read and mark as read if needed
|
// Save last page read and mark as read if needed
|
||||||
viewModelScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
updateChapterProgress(page.index)
|
updateChapterProgress(selectedChapter, page.index)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selectedChapter != getCurrentChapter()) {
|
if (selectedChapter != getCurrentChapter()) {
|
||||||
@ -606,9 +588,7 @@ class ReaderViewModel(
|
|||||||
* Saves the chapter progress (last read page and whether it's read)
|
* Saves the chapter progress (last read page and whether it's read)
|
||||||
* if incognito mode isn't on.
|
* if incognito mode isn't on.
|
||||||
*/
|
*/
|
||||||
private suspend fun updateChapterProgress(pageIndex: Int) {
|
private suspend fun updateChapterProgress(readerChapter: ReaderChapter, pageIndex: Int) {
|
||||||
val readerChapter = getCurrentChapter() ?: return
|
|
||||||
|
|
||||||
mutableState.update {
|
mutableState.update {
|
||||||
it.copy(currentPage = pageIndex + 1)
|
it.copy(currentPage = pageIndex + 1)
|
||||||
}
|
}
|
||||||
@ -654,18 +634,19 @@ class ReaderViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun flushReadTimer() {
|
fun flushReadTimer() {
|
||||||
viewModelScope.launchNonCancellable {
|
getCurrentChapter()?.let {
|
||||||
updateHistory()
|
viewModelScope.launchNonCancellable {
|
||||||
|
updateHistory(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the chapter last read history if incognito mode isn't on.
|
* Saves the chapter last read history if incognito mode isn't on.
|
||||||
*/
|
*/
|
||||||
private suspend fun updateHistory() {
|
private suspend fun updateHistory(readerChapter: ReaderChapter) {
|
||||||
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
|
||||||
@ -1109,44 +1090,14 @@ class ReaderViewModel(
|
|||||||
* will run in a background thread and errors are ignored.
|
* will run in a background thread and errors are ignored.
|
||||||
*/
|
*/
|
||||||
private fun updateTrackChapterRead(readerChapter: ReaderChapter) {
|
private fun updateTrackChapterRead(readerChapter: ReaderChapter) {
|
||||||
if (incognitoMode || !hasTrackers) return
|
if (incognitoMode) return
|
||||||
if (!trackPreferences.autoUpdateTrack().get()) return
|
if (!trackPreferences.autoUpdateTrack().get()) return
|
||||||
|
|
||||||
val manga = manga ?: return
|
val manga = manga ?: return
|
||||||
val chapterRead = readerChapter.chapter.chapter_number.toDouble()
|
|
||||||
|
|
||||||
val trackManager = Injekt.get<TrackManager>()
|
|
||||||
val context = Injekt.get<Application>()
|
val context = Injekt.get<Application>()
|
||||||
|
|
||||||
viewModelScope.launchNonCancellable {
|
viewModelScope.launchNonCancellable {
|
||||||
getTracks.await(manga.id)
|
trackChapter.await(context, manga.id, readerChapter.chapter.chapter_number.toDouble())
|
||||||
.mapNotNull { track ->
|
|
||||||
val service = trackManager.getService(track.syncId)
|
|
||||||
if (service != null && service.isLogged && chapterRead > track.lastChapterRead /* SY --> */ && ((service.id == TrackManager.MDLIST && track.status != FollowStatus.UNFOLLOWED.int.toLong()) || service.id != TrackManager.MDLIST)/* SY <-- */) {
|
|
||||||
val updatedTrack = track.copy(lastChapterRead = chapterRead)
|
|
||||||
|
|
||||||
// We want these to execute even if the presenter is destroyed and leaks
|
|
||||||
// for a while. The view can still be garbage collected.
|
|
||||||
async {
|
|
||||||
runCatching {
|
|
||||||
try {
|
|
||||||
if (!context.isOnline()) error("Couldn't update tracker as device is offline")
|
|
||||||
service.update(updatedTrack.toDbTrack(), true)
|
|
||||||
insertTrack.await(updatedTrack)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
delayedTrackingStore.addItem(updatedTrack)
|
|
||||||
DelayedTrackingUpdateJob.setupTask(context)
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.awaitAll()
|
|
||||||
.mapNotNull { it.exceptionOrNull() }
|
|
||||||
.forEach { logcat(LogPriority.INFO, it) }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user