diff --git a/app/src/main/java/eu/kanade/domain/DomainModule.kt b/app/src/main/java/eu/kanade/domain/DomainModule.kt index f3aa29464..58faf2914 100644 --- a/app/src/main/java/eu/kanade/domain/DomainModule.kt +++ b/app/src/main/java/eu/kanade/domain/DomainModule.kt @@ -16,6 +16,7 @@ import eu.kanade.domain.source.interactor.SetMigrateSorting import eu.kanade.domain.source.interactor.ToggleLanguage import eu.kanade.domain.source.interactor.ToggleSource import eu.kanade.domain.source.interactor.ToggleSourcePin +import eu.kanade.domain.track.interactor.TrackChapter import tachiyomi.data.category.CategoryRepositoryImpl import tachiyomi.data.chapter.ChapterRepositoryImpl import tachiyomi.data.history.HistoryRepositoryImpl @@ -109,6 +110,7 @@ class DomainModule : InjektModule { addFactory { GetApplicationRelease(get(), get()) } addSingletonFactory { TrackRepositoryImpl(get()) } + addFactory { TrackChapter(get(), get(), get(), get()) } addFactory { DeleteTrack(get()) } addFactory { GetTracksPerManga(get()) } addFactory { GetTracks(get()) } diff --git a/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt new file mode 100644 index 000000000..57a49006a --- /dev/null +++ b/app/src/main/java/eu/kanade/domain/track/interactor/TrackChapter.kt @@ -0,0 +1,56 @@ +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 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) { + 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) } + } + } +} diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt index 57c6d61df..284005d33 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt @@ -10,10 +10,8 @@ import eu.kanade.domain.chapter.model.toDbChapter import eu.kanade.domain.manga.interactor.SetMangaViewerFlags import eu.kanade.domain.manga.model.orientationType import eu.kanade.domain.manga.model.readingModeType -import eu.kanade.domain.track.model.toDbTrack -import eu.kanade.domain.track.service.DelayedTrackingUpdateJob +import eu.kanade.domain.track.interactor.TrackChapter import eu.kanade.domain.track.service.TrackPreferences -import eu.kanade.domain.track.store.DelayedTrackingStore import eu.kanade.tachiyomi.data.database.models.toDomainChapter import eu.kanade.tachiyomi.data.download.DownloadManager import eu.kanade.tachiyomi.data.download.DownloadProvider @@ -21,7 +19,6 @@ import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.data.saver.Image import eu.kanade.tachiyomi.data.saver.ImageSaver 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.online.HttpSource import eu.kanade.tachiyomi.ui.reader.loader.ChapterLoader @@ -42,11 +39,8 @@ import eu.kanade.tachiyomi.util.lang.byteSize import eu.kanade.tachiyomi.util.lang.takeBytes import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.cacheImageDir -import eu.kanade.tachiyomi.util.system.isOnline import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.async -import kotlinx.coroutines.awaitAll import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -75,8 +69,6 @@ import tachiyomi.domain.history.model.HistoryUpdate import tachiyomi.domain.manga.interactor.GetManga import tachiyomi.domain.manga.model.Manga import tachiyomi.domain.source.service.SourceManager -import tachiyomi.domain.track.interactor.GetTracks -import tachiyomi.domain.track.interactor.InsertTrack import tachiyomi.source.local.isLocal import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get @@ -96,12 +88,10 @@ class ReaderViewModel( private val basePreferences: BasePreferences = Injekt.get(), private val downloadPreferences: DownloadPreferences = 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 getChapterByMangaId: GetChapterByMangaId = 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 updateChapter: UpdateChapter = Injekt.get(), private val setMangaViewerFlags: SetMangaViewerFlags = Injekt.get(), @@ -197,12 +187,6 @@ class ReaderViewModel( .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 downloadAheadAmount = downloadPreferences.autoDownloadWhileReading().get() @@ -258,8 +242,6 @@ class ReaderViewModel( mutableState.update { it.copy(manga = manga) } if (chapterId == -1L) chapterId = initialChapterId - checkTrackers(manga) - val context = Injekt.get() val source = sourceManager.getOrStub(manga.source) loader = ChapterLoader(context, downloadManager, downloadProvider, manga, source) @@ -419,7 +401,7 @@ class ReaderViewModel( // Save last page read and mark as read if needed viewModelScope.launchNonCancellable { - updateChapterProgress(page.index) + updateChapterProgress(selectedChapter, page.index) } if (selectedChapter != getCurrentChapter()) { @@ -499,9 +481,7 @@ class ReaderViewModel( * Saves the chapter progress (last read page and whether it's read) * if incognito mode isn't on. */ - private suspend fun updateChapterProgress(pageIndex: Int) { - val readerChapter = getCurrentChapter() ?: return - + private suspend fun updateChapterProgress(readerChapter: ReaderChapter, pageIndex: Int) { mutableState.update { it.copy(currentPage = pageIndex + 1) } @@ -532,18 +512,19 @@ class ReaderViewModel( } fun flushReadTimer() { - viewModelScope.launchNonCancellable { - updateHistory() + getCurrentChapter()?.let { + viewModelScope.launchNonCancellable { + updateHistory(it) + } } } /** * 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 - val readerChapter = getCurrentChapter() ?: return val chapterId = readerChapter.chapter.id!! val endTime = Date() val sessionReadDuration = chapterReadStartTime?.let { endTime.time - it } ?: 0 @@ -829,44 +810,14 @@ class ReaderViewModel( * will run in a background thread and errors are ignored. */ private fun updateTrackChapterRead(readerChapter: ReaderChapter) { - if (incognitoMode || !hasTrackers) return + if (incognitoMode) return if (!trackPreferences.autoUpdateTrack().get()) return val manga = manga ?: return - val chapterRead = readerChapter.chapter.chapter_number.toDouble() - - val trackManager = Injekt.get() val context = Injekt.get() viewModelScope.launchNonCancellable { - getTracks.await(manga.id) - .mapNotNull { track -> - val service = trackManager.getService(track.syncId) - if (service != null && service.isLogged && chapterRead > track.lastChapterRead) { - 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) } + trackChapter.await(context, manga.id, readerChapter.chapter.chapter_number.toDouble()) } }