From 726613e6d73cadffd785390483bdf9fa575a56cd Mon Sep 17 00:00:00 2001 From: Ahmad Ansori Palembani Date: Fri, 29 Nov 2024 18:02:45 +0700 Subject: [PATCH] refactor(db): Migrate track queries (that can be migrated) to SQLDelight --- .../data/database/queries/TrackQueries.kt | 3 +- .../data/library/LibraryUpdateJob.kt | 7 +++- .../data/track/DelayedTrackingUpdateJob.kt | 5 ++- .../tachiyomi/ui/library/LibraryController.kt | 2 +- .../ui/manga/MangaDetailsController.kt | 2 +- .../ui/manga/MangaDetailsPresenter.kt | 12 +++--- .../tachiyomi/ui/reader/ReaderViewModel.kt | 2 +- .../kanade/tachiyomi/util/MangaExtensions.kt | 37 ++++++++++--------- .../util/chapter/ChapterTrackSync.kt | 14 +++---- .../main/java/yokai/core/di/DomainModule.kt | 2 + .../yokai/data/track/TrackRepositoryImpl.kt | 18 +++++++++ .../yokai/domain/track/TrackRepository.kt | 1 + .../domain/track/interactor/InsertTrack.kt | 10 +++++ .../sqldelight/tachiyomi/data/manga_sync.sq | 23 +++++++++++- 14 files changed, 98 insertions(+), 40 deletions(-) create mode 100644 app/src/main/java/yokai/domain/track/interactor/InsertTrack.kt diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/TrackQueries.kt b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/TrackQueries.kt index 74f8c6059d..693432fddb 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/TrackQueries.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/database/queries/TrackQueries.kt @@ -20,8 +20,7 @@ interface TrackQueries : DbProvider { ) .prepare() - fun insertTrack(track: Track) = db.put().`object`(track).prepare() - + // FIXME: Migrate to SQLDelight, on halt: in StorIO transaction fun insertTracks(tracks: List) = db.put().objects(tracks).prepare() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt index 96e3e85c22..8b3e9459df 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateJob.kt @@ -81,12 +81,14 @@ import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get +import uy.kohesive.injekt.injectLazy import yokai.domain.category.interactor.GetCategories import yokai.domain.chapter.interactor.GetChapter import yokai.domain.manga.interactor.GetLibraryManga import yokai.domain.manga.interactor.UpdateManga import yokai.domain.manga.models.cover import yokai.domain.track.interactor.GetTrack +import yokai.domain.track.interactor.InsertTrack import yokai.i18n.MR import yokai.util.lang.getString @@ -107,6 +109,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet private val getLibraryManga: GetLibraryManga = Injekt.get() private val updateManga: UpdateManga = Injekt.get() private val getTrack: GetTrack = Injekt.get() + private val insertTrack: InsertTrack by injectLazy() private var extraDeferredJobs = mutableListOf>() @@ -322,9 +325,9 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet if (service != null && service in loggedServices) { try { val newTrack = service.refresh(track) - db.insertTrack(newTrack).executeAsBlocking() + insertTrack.await(newTrack) - syncChaptersWithTrackServiceTwoWay(db, getChapter.awaitAll(manga.id!!, false), track, service) + syncChaptersWithTrackServiceTwoWay(getChapter.awaitAll(manga.id!!, false), track, service) } catch (e: Exception) { Logger.e(e) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/data/track/DelayedTrackingUpdateJob.kt b/app/src/main/java/eu/kanade/tachiyomi/data/track/DelayedTrackingUpdateJob.kt index b0320c25c5..b8bbadfde3 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/data/track/DelayedTrackingUpdateJob.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/data/track/DelayedTrackingUpdateJob.kt @@ -12,7 +12,6 @@ import androidx.work.WorkerParameters import co.touchlab.kermit.Logger import eu.kanade.tachiyomi.data.database.DatabaseHelper import eu.kanade.tachiyomi.data.preference.PreferencesHelper -import eu.kanade.tachiyomi.util.system.e import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext @@ -21,12 +20,14 @@ import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import yokai.domain.manga.interactor.GetManga import yokai.domain.track.interactor.GetTrack +import yokai.domain.track.interactor.InsertTrack class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters) : CoroutineWorker(context, workerParams) { private val getManga: GetManga by injectLazy() private val getTrack: GetTrack by injectLazy() + private val insertTrack: InsertTrack by injectLazy() override suspend fun doWork(): Result { val preferences = Injekt.get() @@ -56,7 +57,7 @@ class DelayedTrackingUpdateJob(context: Context, workerParams: WorkerParameters) try { track.last_chapter_read = trackChapter.second service.update(track, true) - db.insertTrack(track).executeAsBlocking() + insertTrack.await(track) } catch (e: Exception) { Logger.e(e) { "Unable to update tracker [tracker id ${track.sync_id}]" } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt index 0c08e976ff..77a468109c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/library/LibraryController.kt @@ -2191,7 +2191,7 @@ open class LibraryController( */ private fun showChangeMangaCategoriesSheet() { val activity = activity ?: return - selectedMangas.toList().moveCategories(presenter.db, activity) { + selectedMangas.toList().moveCategories(activity) { presenter.getLibrary() destroyActionModeIfNeeded() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt index d71502a10b..de13d68d0c 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaDetailsController.kt @@ -1634,7 +1634,7 @@ class MangaDetailsController : private fun showCategoriesSheet() { val adding = !presenter.manga.favorite - presenter.manga.moveCategories(presenter.db, activity!!, adding) { + presenter.manga.moveCategories(activity!!, adding) { updateHeader() if (adding) { showAddedSnack() 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 9c314ca080..247f5df6df 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 @@ -97,6 +97,7 @@ import yokai.domain.manga.models.cover import yokai.domain.storage.StorageManager import yokai.domain.track.interactor.DeleteTrack import yokai.domain.track.interactor.GetTrack +import yokai.domain.track.interactor.InsertTrack import yokai.i18n.MR import yokai.util.lang.getString @@ -118,6 +119,7 @@ class MangaDetailsPresenter( private val updateManga: UpdateManga by injectLazy() private val deleteTrack: DeleteTrack by injectLazy() private val getTrack: GetTrack by injectLazy() + private val insertTrack: InsertTrack by injectLazy() private val getHistory: GetHistory by injectLazy() private val networkPreferences: NetworkPreferences by injectLazy() @@ -1020,8 +1022,8 @@ class MangaDetailsPresenter( null } if (trackItem != null) { - db.insertTrack(trackItem).executeAsBlocking() - syncChaptersWithTrackServiceTwoWay(db, chapters, trackItem, item.service) + insertTrack.await(trackItem) + syncChaptersWithTrackServiceTwoWay(chapters, trackItem, item.service) trackItem } else { item.track @@ -1061,10 +1063,10 @@ class MangaDetailsPresenter( } withContext(Dispatchers.IO) { if (binding != null) { - db.insertTrack(binding).executeAsBlocking() + insertTrack.await(binding) } - syncChaptersWithTrackServiceTwoWay(db, chapters, item, service) + syncChaptersWithTrackServiceTwoWay(chapters, item, service) } fetchTracks() } @@ -1092,7 +1094,7 @@ class MangaDetailsPresenter( null } if (binding != null) { - withContext(Dispatchers.IO) { db.insertTrack(binding).executeAsBlocking() } + withContext(Dispatchers.IO) { insertTrack.await(binding) } fetchTracks() } else { trackRefreshDone() 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 2bfd8bc7f4..0b1cf2e5f9 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 @@ -998,7 +998,7 @@ class ReaderViewModel( launchIO { val newChapterRead = readerChapter.chapter.chapter_number - val errors = updateTrackChapterRead(db, preferences, manga?.id, newChapterRead, true) + val errors = updateTrackChapterRead(preferences, manga?.id, newChapterRead, true) if (errors.isNotEmpty()) { eventChannel.send(Event.ShareTrackingError(errors)) } diff --git a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt index d38addd53f..06c6e074a9 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt @@ -51,6 +51,7 @@ import yokai.domain.chapter.interactor.GetChapter import yokai.domain.manga.interactor.GetManga import yokai.domain.manga.interactor.UpdateManga import yokai.domain.manga.models.MangaUpdate +import yokai.domain.track.interactor.InsertTrack import yokai.i18n.MR import yokai.util.lang.getString import android.R as AR @@ -83,12 +84,11 @@ suspend fun Manga.shouldDownloadNewChapters(prefs: PreferencesHelper, getCategor return categoriesForManga.any { it in includedCategories } } -fun Manga.moveCategories(db: DatabaseHelper, activity: Activity, onMangaMoved: () -> Unit) { - moveCategories(db, activity, false, onMangaMoved) +fun Manga.moveCategories(activity: Activity, onMangaMoved: () -> Unit) { + moveCategories(activity, false, onMangaMoved) } fun Manga.moveCategories( - db: DatabaseHelper, activity: Activity, addingToLibrary: Boolean, onMangaMoved: () -> Unit, @@ -110,13 +110,12 @@ fun Manga.moveCategories( ) { onMangaMoved() if (addingToLibrary) { - autoAddTrack(db, onMangaMoved) + autoAddTrack(onMangaMoved) } }.show() } fun List.moveCategories( - db: DatabaseHelper, activity: Activity, onMangaMoved: () -> Unit, ) { @@ -211,7 +210,7 @@ fun Manga.addOrRemoveToFavorites( defaultCategory != null -> { favorite = true date_added = Date().time - autoAddTrack(db, onMangaMoved) + autoAddTrack(onMangaMoved) // FIXME: Don't do blocking runBlocking { updateManga.await( @@ -228,7 +227,7 @@ fun Manga.addOrRemoveToFavorites( onMangaMoved() return view.snack(activity.getString(MR.strings.added_to_, defaultCategory.name)) { setAction(MR.strings.change) { - moveCategories(db, activity, onMangaMoved) + moveCategories(activity, onMangaMoved) } } } @@ -238,7 +237,7 @@ fun Manga.addOrRemoveToFavorites( ) -> { // last used category(s) favorite = true date_added = Date().time - autoAddTrack(db, onMangaMoved) + autoAddTrack(onMangaMoved) // FIXME: Don't do blocking runBlocking { updateManga.await( @@ -270,14 +269,14 @@ fun Manga.addOrRemoveToFavorites( ), ) { setAction(MR.strings.change) { - moveCategories(db, activity, onMangaMoved) + moveCategories(activity, onMangaMoved) } } } defaultCategoryId == 0 || categories.isEmpty() -> { // 'Default' or no category favorite = true date_added = Date().time - autoAddTrack(db, onMangaMoved) + autoAddTrack(onMangaMoved) // FIXME: Don't do blocking runBlocking { updateManga.await( @@ -294,7 +293,7 @@ fun Manga.addOrRemoveToFavorites( return if (categories.isNotEmpty()) { view.snack(activity.getString(MR.strings.added_to_, activity.getString(MR.strings.default_value))) { setAction(MR.strings.change) { - moveCategories(db, activity, onMangaMoved) + moveCategories(activity, onMangaMoved) } } } else { @@ -302,7 +301,7 @@ fun Manga.addOrRemoveToFavorites( } } else -> { // Always ask - showSetCategoriesSheet(db, activity, categories, onMangaAdded, onMangaMoved) + showSetCategoriesSheet(activity, categories, onMangaAdded, onMangaMoved) } } } else { @@ -352,7 +351,6 @@ fun Manga.addOrRemoveToFavorites( } private fun Manga.showSetCategoriesSheet( - db: DatabaseHelper, activity: Activity, categories: List, onMangaAdded: (Pair?) -> Unit, @@ -372,7 +370,7 @@ private fun Manga.showSetCategoriesSheet( ) { (activity as? MainActivity)?.showNotificationPermissionPrompt() onMangaAdded(null) - autoAddTrack(db, onMangaMoved) + autoAddTrack(onMangaMoved) }.show() } @@ -470,10 +468,11 @@ private fun showAddDuplicateDialog( }.show() } -fun Manga.autoAddTrack(db: DatabaseHelper, onMangaMoved: () -> Unit) { +fun Manga.autoAddTrack(onMangaMoved: () -> Unit) { val loggedServices = Injekt.get().services.filter { it.isLogged } val source = Injekt.get().getOrStub(this.source) val getChapter = Injekt.get() + val insertTrack = Injekt.get() loggedServices .filterIsInstance() .filter { it.accept(source) } @@ -484,9 +483,13 @@ fun Manga.autoAddTrack(db: DatabaseHelper, onMangaMoved: () -> Unit) { val mangaId = this@autoAddTrack.id!! track.manga_id = mangaId (service as TrackService).bind(track) - db.insertTrack(track).executeAsBlocking() + insertTrack.await(track) - syncChaptersWithTrackServiceTwoWay(db, getChapter.awaitAll(mangaId, false), track, service as TrackService) + syncChaptersWithTrackServiceTwoWay( + getChapter.awaitAll(mangaId, false), + track, + service as TrackService + ) withUIContext { onMangaMoved() } 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 45fbf39873..d6b8682e2f 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 @@ -9,7 +9,6 @@ import eu.kanade.tachiyomi.data.track.DelayedTrackingUpdateJob import eu.kanade.tachiyomi.data.track.EnhancedTrackService import eu.kanade.tachiyomi.data.track.TrackManager import eu.kanade.tachiyomi.data.track.TrackService -import eu.kanade.tachiyomi.util.system.e import eu.kanade.tachiyomi.util.system.isOnline import eu.kanade.tachiyomi.util.system.launchIO import eu.kanade.tachiyomi.util.system.w @@ -20,6 +19,7 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import yokai.domain.chapter.interactor.UpdateChapter import yokai.domain.track.interactor.GetTrack +import yokai.domain.track.interactor.InsertTrack /** * Helper method for syncing a remote track with the local chapters, and back @@ -30,11 +30,11 @@ import yokai.domain.track.interactor.GetTrack * @param service the tracker service. */ suspend fun syncChaptersWithTrackServiceTwoWay( - db: DatabaseHelper, chapters: List, remoteTrack: Track, service: TrackService, updateChapter: UpdateChapter = Injekt.get(), + insertTrack: InsertTrack = Injekt.get() ) = withIOContext { if (service !is EnhancedTrackService) { return@withIOContext @@ -54,7 +54,7 @@ suspend fun syncChaptersWithTrackServiceTwoWay( try { service.update(remoteTrack) - db.insertTrack(remoteTrack).executeAsBlocking() + insertTrack.await(remoteTrack) } catch (e: Throwable) { Logger.w(e) } @@ -86,7 +86,7 @@ fun updateTrackChapterMarkedAsRead( // We want these to execute even if the presenter is destroyed trackingJobs[mangaId] = launchIO { delay(delay) - updateTrackChapterRead(db, preferences, mangaId, newChapterRead) + updateTrackChapterRead(preferences, mangaId, newChapterRead) fetchTracks?.invoke() trackingJobs.remove(mangaId) } to newChapterRead @@ -94,12 +94,12 @@ fun updateTrackChapterMarkedAsRead( } suspend fun updateTrackChapterRead( - db: DatabaseHelper, preferences: PreferencesHelper, mangaId: Long?, newChapterRead: Float, retryWhenOnline: Boolean = false, - getTrack: GetTrack = Injekt.get() + getTrack: GetTrack = Injekt.get(), + insertTrack: InsertTrack = Injekt.get(), ): List> { val trackManager = Injekt.get() val trackList = getTrack.awaitAllByMangaId(mangaId) @@ -113,7 +113,7 @@ suspend fun updateTrackChapterRead( try { track.last_chapter_read = newChapterRead service.update(track, true) - db.insertTrack(track).executeAsBlocking() + insertTrack.await(track) } catch (e: Exception) { Logger.e(e) { "Unable to update tracker [tracker id ${track.sync_id}]" } failures.add(service to e.localizedMessage) diff --git a/app/src/main/java/yokai/core/di/DomainModule.kt b/app/src/main/java/yokai/core/di/DomainModule.kt index 29e7572cf5..d54f9d3b89 100644 --- a/app/src/main/java/yokai/core/di/DomainModule.kt +++ b/app/src/main/java/yokai/core/di/DomainModule.kt @@ -44,6 +44,7 @@ import yokai.domain.recents.interactor.GetRecents import yokai.domain.track.TrackRepository import yokai.domain.track.interactor.DeleteTrack import yokai.domain.track.interactor.GetTrack +import yokai.domain.track.interactor.InsertTrack fun domainModule() = module { factory { TrustExtension(get(), get()) } @@ -90,4 +91,5 @@ fun domainModule() = module { single { TrackRepositoryImpl(get()) } factory { DeleteTrack(get()) } factory { GetTrack(get()) } + factory { InsertTrack(get()) } } diff --git a/app/src/main/java/yokai/data/track/TrackRepositoryImpl.kt b/app/src/main/java/yokai/data/track/TrackRepositoryImpl.kt index cdcc443155..604ef38c15 100644 --- a/app/src/main/java/yokai/data/track/TrackRepositoryImpl.kt +++ b/app/src/main/java/yokai/data/track/TrackRepositoryImpl.kt @@ -10,4 +10,22 @@ class TrackRepositoryImpl(private val handler: DatabaseHandler) : TrackRepositor override suspend fun deleteForManga(mangaId: Long, syncId: Long) = handler.await { manga_syncQueries.deleteForManga(mangaId, syncId) } + + override suspend fun insert(track: Track) = + handler.await { + manga_syncQueries.insert( + mangaId = track.manga_id, + syncId = track.sync_id, + remoteId = track.media_id, + libraryId = track.library_id, + title = track.title, + lastChapterRead = track.last_chapter_read.toDouble(), + totalChapters = track.total_chapters, + status = track.status.toLong(), + score = track.score.toDouble(), + remoteUrl = track.tracking_url, + startDate = track.started_reading_date, + finishDate = track.finished_reading_date, + ) + } } diff --git a/app/src/main/java/yokai/domain/track/TrackRepository.kt b/app/src/main/java/yokai/domain/track/TrackRepository.kt index d776e5be51..d6d704940f 100644 --- a/app/src/main/java/yokai/domain/track/TrackRepository.kt +++ b/app/src/main/java/yokai/domain/track/TrackRepository.kt @@ -5,4 +5,5 @@ import eu.kanade.tachiyomi.data.database.models.Track interface TrackRepository { suspend fun getAllByMangaId(mangaId: Long): List suspend fun deleteForManga(mangaId: Long, syncId: Long) + suspend fun insert(track: Track) } diff --git a/app/src/main/java/yokai/domain/track/interactor/InsertTrack.kt b/app/src/main/java/yokai/domain/track/interactor/InsertTrack.kt new file mode 100644 index 0000000000..57358ab071 --- /dev/null +++ b/app/src/main/java/yokai/domain/track/interactor/InsertTrack.kt @@ -0,0 +1,10 @@ +package yokai.domain.track.interactor + +import eu.kanade.tachiyomi.data.database.models.Track +import yokai.domain.track.TrackRepository + +class InsertTrack( + private val trackRepository: TrackRepository, +) { + suspend fun await(track: Track) = trackRepository.insert(track) +} diff --git a/data/src/commonMain/sqldelight/tachiyomi/data/manga_sync.sq b/data/src/commonMain/sqldelight/tachiyomi/data/manga_sync.sq index aad779715f..9039301847 100644 --- a/data/src/commonMain/sqldelight/tachiyomi/data/manga_sync.sq +++ b/data/src/commonMain/sqldelight/tachiyomi/data/manga_sync.sq @@ -1,5 +1,3 @@ -import kotlin.Float; - CREATE TABLE manga_sync( _id INTEGER NOT NULL PRIMARY KEY, manga_id INTEGER NOT NULL, @@ -27,3 +25,24 @@ WHERE manga_id = :mangaId; deleteForManga: DELETE FROM manga_sync WHERE manga_id = :mangaId AND sync_id = :syncId; + +insert: +INSERT INTO manga_sync(manga_id, sync_id, remote_id, library_id, title, last_chapter_read, total_chapters, status, score, remote_url, start_date, finish_date) +VALUES(:mangaId, :syncId, :remoteId, :libraryId, :title, :lastChapterRead, :totalChapters, :status, :score, :remoteUrl, :startDate, :finishDate); + +update: +UPDATE manga_sync +SET + manga_id = coalesce(:mangaId, manga_id), + sync_id = coalesce(:syncId, sync_id), + remote_id = coalesce(:remoteId, remote_id), + library_id = coalesce(:libraryId, library_id), + title = coalesce(:title, title), + last_chapter_read = coalesce(:lastChapterRead, last_chapter_read), + total_chapters = coalesce(:totalChapters, total_chapters), + status = coalesce(:status, status), + score = coalesce(:score, score), + remote_url = coalesce(:remoteUrl, remote_url), + start_date = coalesce(:startDate, start_date), + finish_date = coalesce(:finishDate, finish_date) +WHERE _id = :id;