refactor(db): Migrate track queries (that can be migrated) to SQLDelight

This commit is contained in:
Ahmad Ansori Palembani 2024-11-29 18:02:45 +07:00
parent 93ec13f324
commit 726613e6d7
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
14 changed files with 98 additions and 40 deletions

View file

@ -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<Track>) = db.put().objects(tracks).prepare()
}

View file

@ -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<Deferred<Any>>()
@ -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)
}

View file

@ -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<PreferencesHelper>()
@ -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}]" }
}

View file

@ -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()
}

View file

@ -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()

View file

@ -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()

View file

@ -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))
}

View file

@ -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<Manga>.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<Category>,
onMangaAdded: (Pair<Long, Boolean>?) -> 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<TrackManager>().services.filter { it.isLogged }
val source = Injekt.get<SourceManager>().getOrStub(this.source)
val getChapter = Injekt.get<GetChapter>()
val insertTrack = Injekt.get<InsertTrack>()
loggedServices
.filterIsInstance<EnhancedTrackService>()
.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()
}

View file

@ -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<Chapter>,
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<Pair<TrackService, String?>> {
val trackManager = Injekt.get<TrackManager>()
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)

View file

@ -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<TrackRepository> { TrackRepositoryImpl(get()) }
factory { DeleteTrack(get()) }
factory { GetTrack(get()) }
factory { InsertTrack(get()) }
}

View file

@ -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,
)
}
}

View file

@ -5,4 +5,5 @@ import eu.kanade.tachiyomi.data.database.models.Track
interface TrackRepository {
suspend fun getAllByMangaId(mangaId: Long): List<Track>
suspend fun deleteForManga(mangaId: Long, syncId: Long)
suspend fun insert(track: Track)
}

View file

@ -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)
}

View file

@ -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;