From 1e68e55cf7722de5d4ff5375bb189b06762ade2b Mon Sep 17 00:00:00 2001 From: Ahmad Ansori Palembani Date: Sat, 17 Aug 2024 07:59:39 +0700 Subject: [PATCH] refactor(chapter): Migrate more queries to SQLDelight --- .../create/creators/MangaBackupCreator.kt | 10 ++-- .../restore/restorers/MangaBackupRestorer.kt | 6 ++- .../tachiyomi/data/database/models/Chapter.kt | 10 ++++ .../data/database/queries/ChapterQueries.kt | 46 +------------------ .../tachiyomi/data/download/DownloadStore.kt | 12 +++-- .../data/notification/NotificationReceiver.kt | 41 ++++++++++------- .../ui/manga/MangaDetailsPresenter.kt | 23 ++++++---- .../tachiyomi/ui/reader/ReaderViewModel.kt | 16 ++++++- .../tachiyomi/ui/recents/RecentsPresenter.kt | 4 +- .../util/chapter/ChapterTrackSync.kt | 33 +++++++------ .../data/chapter/ChapterRepositoryImpl.kt | 24 ++++++++++ .../yokai/domain/chapter/ChapterRepository.kt | 6 +++ .../domain/chapter/interactor/GetChapter.kt | 9 ++++ .../sqldelight/tachiyomi/data/chapters.sq | 15 ++++++ 14 files changed, 160 insertions(+), 95 deletions(-) diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt index bf119b4f8c..b3bd7f77a2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/create/creators/MangaBackupCreator.kt @@ -10,12 +10,14 @@ import eu.kanade.tachiyomi.data.library.CustomMangaManager import eu.kanade.tachiyomi.domain.manga.models.Manga import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import yokai.domain.chapter.interactor.GetChapter class MangaBackupCreator( private val db: DatabaseHelper = Injekt.get(), private val customMangaManager: CustomMangaManager = Injekt.get(), + private val getChapter: GetChapter = Injekt.get(), ) { - operator fun invoke(mangas: List, options: BackupOptions): List { + suspend operator fun invoke(mangas: List, options: BackupOptions): List { if (!options.libraryEntries) return emptyList() return mangas.map { @@ -30,14 +32,14 @@ class MangaBackupCreator( * @param options options for the backup * @return [BackupManga] containing manga in a serializable form */ - private fun backupManga(manga: Manga, options: BackupOptions): BackupManga { + private suspend fun backupManga(manga: Manga, options: BackupOptions): BackupManga { // Entry for this manga val mangaObject = BackupManga.copyFrom(manga, if (options.customInfo) customMangaManager else null) // Check if user wants chapter information in backup if (options.chapters) { // Backup all the chapters - val chapters = db.getChapters(manga).executeAsBlocking() + val chapters = manga.id?.let { getChapter.awaitAll(it, false) }.orEmpty() if (chapters.isNotEmpty()) { mangaObject.chapters = chapters.map { BackupChapter.copyFrom(it) } } @@ -65,7 +67,7 @@ class MangaBackupCreator( val historyForManga = db.getHistoryByMangaId(manga.id!!).executeAsBlocking() if (historyForManga.isNotEmpty()) { val history = historyForManga.mapNotNull { history -> - val url = db.getChapter(history.chapter_id).executeAsBlocking()?.url + val url = getChapter.awaitById(history.chapter_id)?.url url?.let { BackupHistory(url, history.last_read, history.time_read) } } if (history.isNotEmpty()) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaBackupRestorer.kt b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaBackupRestorer.kt index 7f985e1293..49b06ea1f4 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaBackupRestorer.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/backup/restore/restorers/MangaBackupRestorer.kt @@ -21,6 +21,7 @@ import uy.kohesive.injekt.api.get import yokai.domain.category.interactor.GetCategories import yokai.domain.chapter.interactor.GetChapter import yokai.domain.chapter.interactor.InsertChapter +import yokai.domain.chapter.interactor.UpdateChapter import yokai.domain.library.custom.model.CustomMangaInfo import yokai.domain.manga.interactor.GetManga import yokai.domain.manga.interactor.InsertManga @@ -32,6 +33,7 @@ class MangaBackupRestorer( private val getCategories: GetCategories = Injekt.get(), private val getChapter: GetChapter = Injekt.get(), private val insertChapter: InsertChapter = Injekt.get(), + private val updateChapter: UpdateChapter = Injekt.get(), private val getManga: GetManga = Injekt.get(), private val insertManga: InsertManga = Injekt.get(), private val updateManga: UpdateManga = Injekt.get(), @@ -138,7 +140,7 @@ class MangaBackupRestorer( } val newChapters = chapters.groupBy { it.id != null } - newChapters[true]?.let { db.updateKnownChaptersBackup(it).executeAsBlocking() } + newChapters[true]?.let { updateChapter.awaitAll(it.map(Chapter::toProgressUpdate)) } newChapters[false]?.let { insertChapter.awaitBulk(it) } } @@ -210,7 +212,7 @@ class MangaBackupRestorer( historyToBeUpdated.add(dbHistory) } else { // If not in database create - db.getChapter(url).executeAsBlocking()?.let { + getChapter.awaitByUrl(url, false)?.let { val historyToAdd = History.create(it).apply { last_read = lastRead time_read = readDuration diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt index 450bc71203..d1ed846c03 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/models/Chapter.kt @@ -2,6 +2,7 @@ package eu.kanade.tachiyomi.data.database.models import eu.kanade.tachiyomi.source.model.SChapter import java.io.Serializable +import yokai.domain.chapter.models.ChapterUpdate interface Chapter : SChapter, Serializable { @@ -24,6 +25,15 @@ interface Chapter : SChapter, Serializable { val isRecognizedNumber: Boolean get() = chapter_number >= 0f + fun toProgressUpdate() = + ChapterUpdate( + id = this.id!!, + read = this.read, + bookmark = this.bookmark, + lastPageRead = this.last_page_read.toLong(), + pagesLeft = this.pages_left.toLong(), + ) + companion object { fun create(): Chapter = ChapterImpl().apply { diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt index 947e1255ca..a391c6673b 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/ChapterQueries.kt @@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.database.queries import com.pushtorefresh.storio.sqlite.queries.Query import eu.kanade.tachiyomi.data.database.DbProvider import eu.kanade.tachiyomi.data.database.models.Chapter -import eu.kanade.tachiyomi.data.database.resolvers.ChapterKnownBackupPutResolver import eu.kanade.tachiyomi.data.database.resolvers.ChapterProgressPutResolver import eu.kanade.tachiyomi.data.database.tables.ChapterTable import eu.kanade.tachiyomi.domain.manga.models.Manga @@ -23,6 +22,7 @@ interface ChapterQueries : DbProvider { ) .prepare() + // FIXME: Migrate to SQLDelight, on halt: in StorIO transaction fun getChapter(id: Long) = db.get() .`object`(Chapter::class.java) .withQuery( @@ -34,54 +34,12 @@ interface ChapterQueries : DbProvider { ) .prepare() - fun getChapter(url: String) = db.get() - .`object`(Chapter::class.java) - .withQuery( - Query.builder() - .table(ChapterTable.TABLE) - .where("${ChapterTable.COL_URL} = ?") - .whereArgs(url) - .build(), - ) - .prepare() - - fun getChapters(url: String) = db.get() - .listOfObjects(Chapter::class.java) - .withQuery( - Query.builder() - .table(ChapterTable.TABLE) - .where("${ChapterTable.COL_URL} = ?") - .whereArgs(url) - .build(), - ) - .prepare() - - fun getChapter(url: String, mangaId: Long) = db.get() - .`object`(Chapter::class.java) - .withQuery( - Query.builder() - .table(ChapterTable.TABLE) - .where("${ChapterTable.COL_URL} = ? AND ${ChapterTable.COL_MANGA_ID} = ?") - .whereArgs(url, mangaId) - .build(), - ) - .prepare() - // FIXME: Migrate to SQLDelight, on halt: in StorIO transaction fun insertChapters(chapters: List) = db.put().objects(chapters).prepare() - fun updateKnownChaptersBackup(chapters: List) = db.put() - .objects(chapters) - .withPutResolver(ChapterKnownBackupPutResolver()) - .prepare() - + // FIXME: Migrate to SQLDelight, on halt: in StorIO transaction fun updateChapterProgress(chapter: Chapter) = db.put() .`object`(chapter) .withPutResolver(ChapterProgressPutResolver()) .prepare() - - fun updateChaptersProgress(chapters: List) = db.put() - .objects(chapters) - .withPutResolver(ChapterProgressPutResolver()) - .prepare() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt index 53fc050c0d..85dabb16c1 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadStore.kt @@ -2,7 +2,6 @@ package eu.kanade.tachiyomi.data.download import android.content.Context import androidx.core.content.edit -import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.download.model.Download import eu.kanade.tachiyomi.domain.manga.models.Manga import eu.kanade.tachiyomi.source.SourceManager @@ -11,6 +10,8 @@ import kotlinx.serialization.Serializable import kotlinx.serialization.encodeToString import kotlinx.serialization.json.Json import uy.kohesive.injekt.injectLazy +import yokai.domain.chapter.interactor.GetChapter +import yokai.domain.manga.interactor.GetManga /** * This class is used to persist active downloads across application restarts. @@ -28,7 +29,8 @@ class DownloadStore( private val preferences = context.getSharedPreferences("active_downloads", Context.MODE_PRIVATE) private val json: Json by injectLazy() - private val db: DatabaseHelper by injectLazy() + private val getChapter: GetChapter by injectLazy() + private val getManga: GetManga by injectLazy() /** * Counter used to keep the queue order. @@ -78,7 +80,7 @@ class DownloadStore( /** * Returns the list of downloads to restore. It should be called in a background thread. */ - fun restore(): List { + suspend fun restore(): List { val objs = preferences.all .mapNotNull { it.value as? String } .mapNotNull { deserialize(it) } @@ -89,10 +91,10 @@ class DownloadStore( val cachedManga = mutableMapOf() for ((mangaId, chapterId) in objs) { val manga = cachedManga.getOrPut(mangaId) { - db.getManga(mangaId).executeAsBlocking() + getManga.awaitById(mangaId) } ?: continue val source = sourceManager.get(manga.source) as? HttpSource ?: continue - val chapter = db.getChapter(chapterId).executeAsBlocking() ?: continue + val chapter = getChapter.awaitById(chapterId) ?: continue downloads.add(Download(source, manga, chapter)) } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt index 2d3a5ab42e..6a3b9d018a 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/notification/NotificationReceiver.kt @@ -8,7 +8,6 @@ import android.content.Intent import android.net.Uri import androidx.core.content.IntentCompat import eu.kanade.tachiyomi.data.backup.restore.BackupRestoreJob -import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.database.models.Chapter import eu.kanade.tachiyomi.data.download.DownloadJob import eu.kanade.tachiyomi.data.download.DownloadManager @@ -28,11 +27,15 @@ import eu.kanade.tachiyomi.util.chapter.updateTrackChapterMarkedAsRead import eu.kanade.tachiyomi.util.storage.DiskUtil import eu.kanade.tachiyomi.util.storage.getUriCompat import eu.kanade.tachiyomi.util.system.getParcelableCompat +import eu.kanade.tachiyomi.util.system.launchIO import eu.kanade.tachiyomi.util.system.notificationManager +import java.io.File import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy -import java.io.File +import yokai.domain.chapter.interactor.GetChapter +import yokai.domain.chapter.interactor.UpdateChapter +import yokai.domain.manga.interactor.GetManga import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID /** @@ -41,6 +44,10 @@ import eu.kanade.tachiyomi.BuildConfig.APPLICATION_ID as ID * NOTE: Use local broadcasts if possible. */ class NotificationReceiver : BroadcastReceiver() { + private val getChapter: GetChapter by injectLazy() + private val updateChapter: UpdateChapter by injectLazy() + private val getManga: GetManga by injectLazy() + /** * Download manager. */ @@ -204,23 +211,25 @@ class NotificationReceiver : BroadcastReceiver() { * @param notificationId id of notification */ private fun markAsRead(chapterUrls: Array, mangaId: Long) { - val db: DatabaseHelper = Injekt.get() val preferences: PreferencesHelper = Injekt.get() - val manga = db.getManga(mangaId).executeAsBlocking() ?: return - val chapters = chapterUrls.map { - val chapter = db.getChapter(it, mangaId).executeAsBlocking() ?: return - chapter.read = true - db.updateChapterProgress(chapter).executeAsBlocking() - if (preferences.removeAfterMarkedAsRead().get()) { - val sourceManager: SourceManager = Injekt.get() - val source = sourceManager.get(manga.source) ?: return - downloadManager.deleteChapters(listOf(chapter), manga, source) + + launchIO { + val manga = getManga.awaitById(mangaId) ?: return@launchIO + val chapters = chapterUrls.map { + val chapter = getChapter.awaitByUrlAndMangaId(it, mangaId, false) ?: return@launchIO + chapter.read = true + updateChapter.await(chapter.toProgressUpdate()) + if (preferences.removeAfterMarkedAsRead().get()) { + val sourceManager: SourceManager = Injekt.get() + val source = sourceManager.get(manga.source) ?: return@launchIO + downloadManager.deleteChapters(listOf(chapter), manga, source) + } + return@map chapter } - return@map chapter + val newLastChapter = chapters.maxByOrNull { it.chapter_number.toInt() } + LibraryUpdateJob.updateMutableFlow.tryEmit(manga.id) + updateTrackChapterMarkedAsRead(Injekt.get(), preferences, newLastChapter, mangaId, 0) } - val newLastChapter = chapters.maxByOrNull { it.chapter_number.toInt() } - LibraryUpdateJob.updateMutableFlow.tryEmit(manga.id) - updateTrackChapterMarkedAsRead(db, preferences, newLastChapter, mangaId, 0) } /** Method called when user wants to stop a restore diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt index 6247e895c1..bdf0438f22 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsPresenter.kt @@ -64,6 +64,11 @@ import eu.kanade.tachiyomi.util.system.launchUI import eu.kanade.tachiyomi.util.system.withIOContext import eu.kanade.tachiyomi.util.system.withUIContext import eu.kanade.tachiyomi.widget.TriStateCheckBox +import java.io.File +import java.io.FileOutputStream +import java.io.OutputStream +import java.util.Date +import java.util.Locale import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.awaitAll @@ -78,16 +83,13 @@ import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import yokai.domain.chapter.interactor.GetAvailableScanlators import yokai.domain.chapter.interactor.GetChapter +import yokai.domain.chapter.interactor.UpdateChapter import yokai.domain.library.custom.model.CustomMangaInfo import yokai.domain.manga.interactor.UpdateManga import yokai.domain.manga.models.MangaUpdate import yokai.domain.storage.StorageManager import yokai.i18n.MR import yokai.util.lang.getString -import java.io.File -import java.io.FileOutputStream -import java.io.OutputStream -import java.util.* class MangaDetailsPresenter( val manga: Manga, @@ -101,6 +103,7 @@ class MangaDetailsPresenter( ) : BaseCoroutinePresenter(), DownloadQueue.DownloadListener { private val getAvailableScanlators: GetAvailableScanlators by injectLazy() private val getChapter: GetChapter by injectLazy() + private val updateChapter: UpdateChapter by injectLazy() private val updateManga: UpdateManga by injectLazy() private val customMangaManager: CustomMangaManager by injectLazy() @@ -508,10 +511,11 @@ class MangaDetailsPresenter( */ fun bookmarkChapters(selectedChapters: List, bookmarked: Boolean) { presenterScope.launch(Dispatchers.IO) { - selectedChapters.forEach { + val updates = selectedChapters.map { it.bookmark = bookmarked + it.toProgressUpdate() } - db.updateChaptersProgress(selectedChapters).executeAsBlocking() + updateChapter.awaitAll(updates) getChapters() withContext(Dispatchers.Main) { view?.updateChapters(chapters) } } @@ -529,15 +533,16 @@ class MangaDetailsPresenter( lastRead: Int? = null, pagesLeft: Int? = null, ) { - presenterScope.launch(Dispatchers.IO) { - selectedChapters.forEach { + presenterScope.launchIO { + val updates = selectedChapters.map { it.read = read if (!read) { it.last_page_read = lastRead ?: 0 it.pages_left = pagesLeft ?: 0 } + it.toProgressUpdate() } - db.updateChaptersProgress(selectedChapters).executeAsBlocking() + updateChapter.awaitAll(updates) if (read && deleteNow && preferences.removeAfterMarkedAsRead().get()) { deleteChapters(selectedChapters, false) } 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 a2e69be766..3f5f9f6721 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 @@ -78,6 +78,8 @@ import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import yokai.domain.chapter.interactor.GetChapter import yokai.domain.chapter.interactor.InsertChapter +import yokai.domain.chapter.interactor.UpdateChapter +import yokai.domain.chapter.models.ChapterUpdate import yokai.domain.download.DownloadPreferences import yokai.domain.manga.interactor.GetManga import yokai.domain.manga.interactor.InsertManga @@ -103,6 +105,7 @@ class ReaderViewModel( ) : ViewModel() { private val getChapter: GetChapter by injectLazy() private val insertChapter: InsertChapter by injectLazy() + private val updateChapter: UpdateChapter by injectLazy() private val getManga: GetManga by injectLazy() private val insertManga: InsertManga by injectLazy() private val updateManga: UpdateManga by injectLazy() @@ -444,7 +447,16 @@ class ReaderViewModel( fun toggleBookmark(chapter: Chapter) { chapter.bookmark = !chapter.bookmark - db.updateChapterProgress(chapter).executeAsBlocking() + viewModelScope.launchNonCancellableIO { + updateChapter.await( + ChapterUpdate( + id = chapter.id!!, + bookmark = chapter.bookmark, + lastPageRead = chapter.last_page_read.toLong(), + pagesLeft = chapter.pages_left.toLong(), + ) + ) + } } /** @@ -617,6 +629,7 @@ class ReaderViewModel( * Saves this [readerChapter]'s progress (last read page and whether it's read). * If incognito mode isn't on or has at least 1 tracker */ + // FIXME: Migrate to SQLDelight, on halt: in StorIO transaction private fun saveChapterProgress(readerChapter: ReaderChapter) { readerChapter.requestedPage = readerChapter.chapter.last_page_read db.getChapter(readerChapter.chapter.id!!).executeAsBlocking()?.let { dbChapter -> @@ -630,6 +643,7 @@ class ReaderViewModel( /** * Saves this [readerChapter] last read history. */ + // FIXME: Migrate to SQLDelight, on halt: in StorIO transaction private fun saveChapterHistory(readerChapter: ReaderChapter) { if (!preferences.incognitoMode().get()) { val readAt = Date().time diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt index 67a4fa35f8..dbbabd3069 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/recents/RecentsPresenter.kt @@ -42,6 +42,7 @@ import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import yokai.domain.chapter.interactor.GetChapter import yokai.domain.chapter.interactor.RecentChapter +import yokai.domain.chapter.interactor.UpdateChapter import yokai.domain.recents.RecentsPreferences import yokai.domain.ui.UiPreferences import yokai.i18n.MR @@ -56,6 +57,7 @@ class RecentsPresenter( ) : BaseCoroutinePresenter(), DownloadQueue.DownloadListener { private val getChapter: GetChapter by injectLazy() private val recentChapter: RecentChapter by injectLazy() + private val updateChapter: UpdateChapter by injectLazy() private var recentsJob: Job? = null var recentItems = listOf() @@ -639,7 +641,7 @@ class RecentsPresenter( pages_left = pagesLeft ?: 0 } } - db.updateChaptersProgress(listOf(chapter)).executeAsBlocking() + updateChapter.await(chapter.toProgressUpdate()) getRecents() } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterTrackSync.kt b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterTrackSync.kt index bdadedf78d..a9bf9e7a53 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterTrackSync.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/chapter/ChapterTrackSync.kt @@ -16,6 +16,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.delay import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import yokai.domain.chapter.interactor.UpdateChapter /** * Helper method for syncing a remote track with the local chapters, and back @@ -25,20 +26,26 @@ import uy.kohesive.injekt.api.get * @param remoteTrack the remote Track object. * @param service the tracker service. */ -fun syncChaptersWithTrackServiceTwoWay(db: DatabaseHelper, chapters: List, remoteTrack: Track, service: TrackService) { - val sortedChapters = chapters.sortedBy { it.chapter_number } - sortedChapters - .filter { chapter -> chapter.chapter_number <= remoteTrack.last_chapter_read && !chapter.read } - .forEach { it.read = true } - db.updateChaptersProgress(sortedChapters).executeAsBlocking() - - // only take into account continuous reading - val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapter_number ?: 0F - - // update remote - remoteTrack.last_chapter_read = localLastRead - +fun syncChaptersWithTrackServiceTwoWay( + db: DatabaseHelper, + chapters: List, + remoteTrack: Track, + service: TrackService, + updateChapter: UpdateChapter = Injekt.get(), +) { launchIO { + val sortedChapters = chapters.sortedBy { it.chapter_number } + sortedChapters + .filter { chapter -> chapter.chapter_number <= remoteTrack.last_chapter_read && !chapter.read } + .forEach { it.read = true } + updateChapter.awaitAll(sortedChapters.map(Chapter::toProgressUpdate)) + + // only take into account continuous reading + val localLastRead = sortedChapters.takeWhile { it.read }.lastOrNull()?.chapter_number ?: 0F + + // update remote + remoteTrack.last_chapter_read = localLastRead + try { service.update(remoteTrack) db.insertTrack(remoteTrack).executeAsBlocking() diff --git a/app/src/main/java/yokai/data/chapter/ChapterRepositoryImpl.kt b/app/src/main/java/yokai/data/chapter/ChapterRepositoryImpl.kt index 84d38b8278..6128154908 100644 --- a/app/src/main/java/yokai/data/chapter/ChapterRepositoryImpl.kt +++ b/app/src/main/java/yokai/data/chapter/ChapterRepositoryImpl.kt @@ -16,9 +16,33 @@ class ChapterRepositoryImpl(private val handler: DatabaseHandler) : ChapterRepos override fun getChaptersAsFlow(mangaId: Long, filterScanlators: Boolean): Flow> = handler.subscribeToList { chaptersQueries.getChaptersByMangaId(mangaId, filterScanlators.toInt().toLong(), Chapter::mapper) } + override suspend fun getChapterById(id: Long): Chapter? = + handler.awaitOneOrNull { chaptersQueries.getChaptersById(id, Chapter::mapper) } + override suspend fun getChaptersByUrl(url: String, filterScanlators: Boolean): List = handler.awaitList { chaptersQueries.getChaptersByUrl(url, filterScanlators.toInt().toLong(), Chapter::mapper) } + override suspend fun getChapterByUrl(url: String, filterScanlators: Boolean): Chapter? = + handler.awaitOneOrNull { chaptersQueries.getChaptersByUrl(url, filterScanlators.toInt().toLong(), Chapter::mapper) } + + override suspend fun getChaptersByUrlAndMangaId( + url: String, + mangaId: Long, + filterScanlators: Boolean + ): List = + handler.awaitList { + chaptersQueries.getChaptersByUrlAndMangaId(url, mangaId, filterScanlators.toInt().toLong(), Chapter::mapper) + } + + override suspend fun getChapterByUrlAndMangaId( + url: String, + mangaId: Long, + filterScanlators: Boolean + ): Chapter? = + handler.awaitOneOrNull { + chaptersQueries.getChaptersByUrlAndMangaId(url, mangaId, filterScanlators.toInt().toLong(), Chapter::mapper) + } + override suspend fun getRecents(filterScanlators: Boolean, search: String, limit: Long, offset: Long): List = handler.awaitList { chaptersQueries.getRecents(search, filterScanlators.toInt().toLong(), limit, offset, MangaChapter::mapper) } diff --git a/app/src/main/java/yokai/domain/chapter/ChapterRepository.kt b/app/src/main/java/yokai/domain/chapter/ChapterRepository.kt index 201333e04d..dbeae5b66a 100644 --- a/app/src/main/java/yokai/domain/chapter/ChapterRepository.kt +++ b/app/src/main/java/yokai/domain/chapter/ChapterRepository.kt @@ -9,7 +9,13 @@ interface ChapterRepository { suspend fun getChapters(mangaId: Long, filterScanlators: Boolean): List fun getChaptersAsFlow(mangaId: Long, filterScanlators: Boolean): Flow> + suspend fun getChapterById(id: Long): Chapter? + suspend fun getChaptersByUrl(url: String, filterScanlators: Boolean): List + suspend fun getChapterByUrl(url: String, filterScanlators: Boolean): Chapter? + + suspend fun getChaptersByUrlAndMangaId(url: String, mangaId: Long, filterScanlators: Boolean): List + suspend fun getChapterByUrlAndMangaId(url: String, mangaId: Long, filterScanlators: Boolean): Chapter? suspend fun getRecents(filterScanlators: Boolean, search: String = "", limit: Long = 25L, offset: Long = 0L): List diff --git a/app/src/main/java/yokai/domain/chapter/interactor/GetChapter.kt b/app/src/main/java/yokai/domain/chapter/interactor/GetChapter.kt index cbf5b9a1ba..01d3b8b42a 100644 --- a/app/src/main/java/yokai/domain/chapter/interactor/GetChapter.kt +++ b/app/src/main/java/yokai/domain/chapter/interactor/GetChapter.kt @@ -11,8 +11,17 @@ class GetChapter( suspend fun awaitAll(manga: Manga, filterScanlators: Boolean? = null) = awaitAll(manga.id!!, filterScanlators ?: (manga.filtered_scanlators?.isNotEmpty() == true)) + suspend fun awaitById(id: Long) = chapterRepository.getChapterById(id) + suspend fun awaitAllByUrl(chapterUrl: String, filterScanlators: Boolean) = chapterRepository.getChaptersByUrl(chapterUrl, filterScanlators) + suspend fun awaitByUrl(chapterUrl: String, filterScanlators: Boolean) = + chapterRepository.getChapterByUrl(chapterUrl, filterScanlators) + + suspend fun awaitAllByUrlAndMangaId(chapterUrl: String, mangaId: Long, filterScanlators: Boolean) = + chapterRepository.getChaptersByUrlAndMangaId(chapterUrl, mangaId, filterScanlators) + suspend fun awaitByUrlAndMangaId(chapterUrl: String, mangaId: Long, filterScanlators: Boolean) = + chapterRepository.getChapterByUrlAndMangaId(chapterUrl, mangaId, filterScanlators) fun subscribeAll(mangaId: Long, filterScanlators: Boolean) = chapterRepository.getChaptersAsFlow(mangaId, filterScanlators) } diff --git a/data/src/commonMain/sqldelight/tachiyomi/data/chapters.sq b/data/src/commonMain/sqldelight/tachiyomi/data/chapters.sq index bb09581ca3..22bc691453 100644 --- a/data/src/commonMain/sqldelight/tachiyomi/data/chapters.sq +++ b/data/src/commonMain/sqldelight/tachiyomi/data/chapters.sq @@ -32,6 +32,10 @@ AND ( :apply_filter = 0 OR S.name IS NULL ); +getChaptersById: +SELECT * FROM chapters +WHERE _id = :id; + getChaptersByUrl: SELECT C.* FROM chapters AS C @@ -43,6 +47,17 @@ AND ( :apply_filter = 0 OR S.name IS NULL ); +getChaptersByUrlAndMangaId: +SELECT C.* +FROM chapters AS C +LEFT JOIN scanlators_view AS S +ON C.manga_id = S.manga_id +AND ifnull(C.scanlator, 'N/A') = ifnull(S.name, '//') -- I assume if it's N/A it shouldn't be filtered +WHERE C.url = :url AND C.manga_id = :manga_id +AND ( + :apply_filter = 0 OR S.name IS NULL +); + getRecents: SELECT M.*,