refactor(db): Migrate GetCategories to SQLDelight

This commit is contained in:
Ahmad Ansori Palembani 2024-11-29 09:52:02 +07:00
parent 97eacbbaea
commit 87bd36d025
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
11 changed files with 92 additions and 80 deletions

View file

@ -1,24 +1,12 @@
package eu.kanade.tachiyomi.data.database.queries
import com.pushtorefresh.storio.sqlite.queries.Query
import com.pushtorefresh.storio.sqlite.queries.RawQuery
import eu.kanade.tachiyomi.data.database.DbProvider
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.tables.CategoryTable
import eu.kanade.tachiyomi.domain.manga.models.Manga
interface CategoryQueries : DbProvider {
fun getCategories() = db.get()
.listOfObjects(Category::class.java)
.withQuery(
Query.builder()
.table(CategoryTable.TABLE)
.orderBy(CategoryTable.COL_ORDER)
.build(),
)
.prepare()
fun getCategoriesForManga(manga: Manga) = db.get()
.listOfObjects(Category::class.java)
.withQuery(

View file

@ -81,6 +81,7 @@ import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.domain.category.interactor.GetCategories
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.manga.interactor.GetLibraryManga
import yokai.domain.manga.interactor.UpdateManga
@ -94,6 +95,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
private val db: DatabaseHelper = Injekt.get()
private val getCategories: GetCategories = Injekt.get()
private val getChapter: GetChapter = Injekt.get()
private val coverCache: CoverCache = Injekt.get()
@ -389,7 +391,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
val httpSource = sourceManager.get(source) as? HttpSource ?: return false
while (count < mangaToUpdateMap[source]!!.size) {
val manga = mangaToUpdateMap[source]!![count]
val shouldDownload = manga.shouldDownloadNewChapters(db, preferences)
val shouldDownload = manga.shouldDownloadNewChapters(preferences)
if (updateMangaChapters(manga, this.count.andIncrement, httpSource, shouldDownload)) {
hasDownloads = true
}
@ -503,7 +505,7 @@ class LibraryUpdateJob(private val context: Context, workerParams: WorkerParamet
categoryIds.addAll(categoriesToUpdate)
libraryManga.filter { it.category in categoriesToUpdate }.distinctBy { it.id }
} else {
categoryIds.addAll(db.getCategories().executeAsBlocking().mapNotNull { it.id } + 0)
categoryIds.addAll(getCategories.await().mapNotNull { it.id } + 0)
libraryManga.distinctBy { it.id }
}
}

View file

@ -8,9 +8,12 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.domain.category.interactor.GetCategories
import yokai.i18n.MR
import yokai.util.lang.getString
@ -21,6 +24,7 @@ class CategoryPresenter(
private val controller: CategoryController,
private val db: DatabaseHelper = Injekt.get(),
) {
private val getCategories: GetCategories by injectLazy()
private var scope = CoroutineScope(Job() + Dispatchers.Default)
@ -39,7 +43,7 @@ class CategoryPresenter(
scope.launch(Dispatchers.IO) {
categories.clear()
categories.add(newCategory())
categories.addAll(db.getCategories().executeAsBlocking())
categories.addAll(getCategories.await())
val catItems = categories.map(::CategoryItem)
withContext(Dispatchers.Main) {
controller.setCategories(catItems)
@ -76,7 +80,8 @@ class CategoryPresenter(
// Insert into database.
cat.mangaSort = LibrarySort.Title.categoryValue
db.insertCategory(cat).executeAsBlocking()
val cats = db.getCategories().executeAsBlocking()
// FIXME: Don't do blocking
val cats = runBlocking { getCategories.await() }
val newCat = cats.find { it.name == name } ?: return false
categories.add(1, newCat)
reorderCategories(categories)

View file

@ -24,7 +24,9 @@ import eu.kanade.tachiyomi.util.view.setPositiveButton
import eu.kanade.tachiyomi.util.view.setTitle
import eu.kanade.tachiyomi.util.view.withFadeTransaction
import eu.kanade.tachiyomi.widget.TriStateCheckBox
import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.injectLazy
import yokai.domain.category.interactor.GetCategories
import android.R as AR
class ManageCategoryDialog(bundle: Bundle? = null) :
@ -40,6 +42,8 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
private val preferences by injectLazy<PreferencesHelper>()
private val db by injectLazy<DatabaseHelper>()
private val getCategories by injectLazy<GetCategories>()
lateinit var binding: MangaCategoryDialogBinding
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
@ -88,7 +92,8 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
) {
category.name = text
if (this.category == null) {
val categories = db.getCategories().executeAsBlocking()
// FIXME: Don't do blocking
val categories = runBlocking { getCategories.await() }
category.order = (categories.maxOfOrNull { it.order } ?: 0) + 1
category.mangaSort = LibrarySort.Title.categoryValue
val dbCategory = db.insertCategory(category).executeAsBlocking()
@ -136,13 +141,14 @@ class ManageCategoryDialog(bundle: Bundle? = null) :
* Returns true if a category with the given name already exists.
*/
private fun categoryExists(name: String): Boolean {
return db.getCategories().executeAsBlocking().any {
// FIXME: Don't do blocking
return runBlocking { getCategories.await() }.any {
it.name.equals(name, true) && category?.id != it.id
}
}
fun onViewCreated() {
if (category?.id ?: 0 <= 0 && category != null) {
if ((category?.id ?: 0) <= 0 && category != null) {
binding.categoryTextLayout.isVisible = false
}
binding.editCategories.isVisible = category != null

View file

@ -33,6 +33,11 @@ import yokai.i18n.MR
import yokai.util.lang.getString
import java.util.*
import kotlin.math.max
import kotlinx.coroutines.runBlocking
import yokai.domain.category.interactor.GetCategories
import yokai.domain.manga.interactor.InsertManga
import yokai.domain.manga.interactor.UpdateManga
import yokai.domain.manga.models.MangaUpdate
class SetCategoriesSheet(
private val activity: Activity,
@ -69,6 +74,9 @@ class SetCategoriesSheet(
private val itemAdapter = ItemAdapter<AddCategoryItem>()
private val db: DatabaseHelper by injectLazy()
private val getCategories: GetCategories by injectLazy()
private val updateManga: UpdateManga by injectLazy()
private val preferences: PreferencesHelper by injectLazy()
override var recyclerView: RecyclerView? = binding.categoryRecyclerView
@ -238,7 +246,8 @@ class SetCategoriesSheet(
binding.cancelButton.setOnClickListener { dismiss() }
binding.newCategoryButton.setOnClickListener {
ManageCategoryDialog(null) {
categories = db.getCategories().executeAsBlocking()
// FIXME: Don't do blocking
categories = runBlocking { getCategories.await() }.toMutableList()
val map = itemAdapter.adapterItems.associate { it.category.id to it.state }
itemAdapter.set(
categories.mapIndexed { index, category ->
@ -263,18 +272,29 @@ class SetCategoriesSheet(
if (listManga.size == 1 && !listManga.first().favorite) {
val manga = listManga.first()
manga.favorite = !manga.favorite
manga.date_added = Date().time
db.insertManga(manga).executeAsBlocking()
// FIXME: Don't do blocking
runBlocking {
updateManga.await(
MangaUpdate(
id = manga.id!!,
favorite = manga.favorite,
dateAdded = manga.date_added,
)
)
}
}
val addCategories = checkedItems.map(AddCategoryItem::category)
val removeCategories = uncheckedItems.map(AddCategoryItem::category)
val mangaCategories = listManga.map { manga ->
val categories = db.getCategoriesForManga(manga).executeAsBlocking()
.subtract(removeCategories.toSet()).plus(addCategories).distinct()
categories.map { MangaCategory.create(manga, it) }
// FIXME: Don't do blocking
runBlocking { getCategories.awaitByMangaId(manga.id!!) }
.subtract(removeCategories.toSet())
.plus(addCategories)
.distinct()
.map { MangaCategory.create(manga, it) }
}.flatten()
if (addCategories.isNotEmpty() || listManga.size == 1) {
Category.lastCategoriesAddedTo =

View file

@ -71,7 +71,6 @@ 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
@ -85,6 +84,7 @@ import kotlinx.coroutines.withContext
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.GetAvailableScanlators
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.chapter.interactor.UpdateChapter
@ -109,6 +109,7 @@ class MangaDetailsPresenter(
private val storageManager: StorageManager = Injekt.get(),
) : BaseCoroutinePresenter<MangaDetailsController>(), DownloadQueue.DownloadListener {
private val getAvailableScanlators: GetAvailableScanlators by injectLazy()
private val getCategories: GetCategories by injectLazy()
private val getChapter: GetChapter by injectLazy()
private val getManga: GetManga by injectLazy()
private val updateChapter: UpdateChapter by injectLazy()
@ -471,7 +472,7 @@ class MangaDetailsPresenter(
if (finChapters.isNotEmpty()) {
val newChapters = withIOContext { syncChaptersWithSource(finChapters, manga, source) }
if (newChapters.first.isNotEmpty()) {
if (manga.shouldDownloadNewChapters(db, preferences)) {
if (manga.shouldDownloadNewChapters(preferences)) {
downloadChapters(
newChapters.first.sortedBy { it.chapter_number }
.map { it.toModel() },
@ -766,28 +767,13 @@ class MangaDetailsPresenter(
}
}
fun toggleFavorite(): Boolean {
manga.favorite = !manga.favorite
when (manga.favorite) {
true -> {
manga.date_added = Date().time
}
false -> manga.date_added = 0
}
db.insertManga(manga).executeAsBlocking()
view?.updateHeader()
return manga.favorite
}
/**
* Get user categories.
*
* @return List of categories, not including the default category
*/
fun getCategories(): List<Category> {
return db.getCategories().executeAsBlocking()
return runBlocking { getCategories.await() }
}
fun confirmDeletion() {

View file

@ -39,6 +39,7 @@ import yokai.util.lang.getString
import java.util.*
import java.util.concurrent.*
import kotlin.math.roundToInt
import yokai.domain.category.interactor.GetCategories
import yokai.domain.track.interactor.GetTrack
class StatsDetailsPresenter(
@ -47,6 +48,7 @@ class StatsDetailsPresenter(
val trackManager: TrackManager = Injekt.get(),
private val sourceManager: SourceManager = Injekt.get(),
) : BaseCoroutinePresenter<StatsDetailsController>() {
private val getCategories: GetCategories by injectLazy()
private val getLibraryManga: GetLibraryManga by injectLazy()
private val getTrack: GetTrack by injectLazy()
@ -275,7 +277,7 @@ class StatsDetailsPresenter(
iconRes = service?.getLogo(),
iconBGColor = service?.getLogoColor(),
readDuration = mangaAndTrack.map { it.first }.getReadDuration(),
id = service?.id?.toLong(),
id = service?.id,
),
)
}
@ -567,7 +569,7 @@ class StatsDetailsPresenter(
}
private fun getCategories(): MutableList<Category> {
return db.getCategories().executeAsBlocking()
return runBlocking { getCategories.await() }.toMutableList()
}
private fun List<LibraryManga>.getReadDuration(): Long {

View file

@ -3,11 +3,8 @@ package eu.kanade.tachiyomi.ui.setting.controllers
import android.content.Intent
import androidx.preference.PreferenceScreen
import com.bluelinelabs.conductor.Controller
import eu.kanade.tachiyomi.R
import yokai.i18n.MR
import yokai.util.lang.getString
import dev.icerock.moko.resources.compose.stringResource
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.preference.changesIn
import eu.kanade.tachiyomi.ui.setting.SettingsLegacyController
@ -23,13 +20,16 @@ import eu.kanade.tachiyomi.ui.setting.titleMRes as titleRes
import eu.kanade.tachiyomi.ui.setting.triStateListPreference
import eu.kanade.tachiyomi.util.lang.addBetaTag
import eu.kanade.tachiyomi.util.view.withFadeTransaction
import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.injectLazy
import yokai.domain.category.interactor.GetCategories
import yokai.domain.download.DownloadPreferences
import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
class SettingsDownloadController : SettingsLegacyController() {
private val db: DatabaseHelper by injectLazy()
private val getCategories: GetCategories by injectLazy()
private val downloadPreferences: DownloadPreferences by injectLazy()
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
@ -55,7 +55,8 @@ class SettingsDownloadController : SettingsLegacyController() {
summaryRes = MR.strings.download_with_id_details
}
val dbCategories = db.getCategories().executeAsBlocking()
// FIXME: Don't do blocking
val dbCategories = runBlocking { getCategories.await() }
val categories = listOf(Category.createDefault(context)) + dbCategories
preferenceCategory {

View file

@ -1,7 +1,6 @@
package eu.kanade.tachiyomi.ui.setting.controllers
import androidx.preference.PreferenceScreen
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.library.LibraryUpdateJob
import eu.kanade.tachiyomi.data.preference.DEVICE_BATTERY_NOT_LOW
@ -30,9 +29,11 @@ import eu.kanade.tachiyomi.ui.setting.triStateListPreference
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.view.withFadeTransaction
import kotlinx.coroutines.runBlocking
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.manga.interactor.GetLibraryManga
import yokai.domain.ui.UiPreferences
import yokai.i18n.MR
@ -43,8 +44,9 @@ import eu.kanade.tachiyomi.ui.setting.titleMRes as titleRes
class SettingsLibraryController : SettingsLegacyController() {
private val db: DatabaseHelper by injectLazy()
private val getLibraryManga: GetLibraryManga by injectLazy()
private val getCategories: GetCategories by injectLazy()
private val uiPreferences: UiPreferences by injectLazy()
override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
@ -89,14 +91,15 @@ class SettingsLibraryController : SettingsLegacyController() {
}
}
val dbCategories = db.getCategories().executeAsBlocking()
// FIXME: Don't do blocking
val dbCategories = runBlocking { getCategories.await() }
preferenceCategory {
titleRes = MR.strings.categories
preference {
key = "edit_categories"
isPersistent = false
val catCount = db.getCategories().executeAsBlocking().size
val catCount = dbCategories.size
titleRes = if (catCount > 0) MR.strings.edit_categories else MR.strings.add_categories
if (catCount > 0) summary = context.getString(MR.plurals.category_plural, catCount, catCount)
onClick { router.pushController(CategoryController().withFadeTransaction()) }

View file

@ -4,7 +4,6 @@ import co.touchlab.kermit.Logger
import eu.davidea.flexibleadapter.items.IFlexible
import eu.kanade.tachiyomi.data.cache.CoverCache
import eu.kanade.tachiyomi.data.database.DatabaseHelper
import eu.kanade.tachiyomi.data.database.models.Category
import eu.kanade.tachiyomi.data.database.models.create
import eu.kanade.tachiyomi.data.database.models.removeCover
import eu.kanade.tachiyomi.data.download.DownloadManager
@ -29,7 +28,6 @@ import eu.kanade.tachiyomi.ui.source.filter.TextItem
import eu.kanade.tachiyomi.ui.source.filter.TextSectionItem
import eu.kanade.tachiyomi.ui.source.filter.TriStateItem
import eu.kanade.tachiyomi.ui.source.filter.TriStateSectionItem
import eu.kanade.tachiyomi.util.system.e
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.withUIContext
import kotlinx.coroutines.Job
@ -344,13 +342,4 @@ open class BrowseSourcePresenter(
}
}
}
/**
* Get user categories.
*
* @return List of categories, not including the default category
*/
fun getCategories(): List<Category> {
return db.getCategories().executeAsBlocking()
}
}

View file

@ -46,6 +46,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.domain.category.interactor.GetCategories
import yokai.domain.chapter.interactor.GetChapter
import yokai.domain.manga.interactor.GetManga
import yokai.i18n.MR
@ -54,7 +55,7 @@ import android.R as AR
fun Manga.isLocal() = source == LocalSource.ID
fun Manga.shouldDownloadNewChapters(db: DatabaseHelper, prefs: PreferencesHelper): Boolean {
suspend fun Manga.shouldDownloadNewChapters(prefs: PreferencesHelper, getCategories: GetCategories = Injekt.get()): Boolean {
if (!favorite) return false
// Boolean to determine if user wants to automatically download new chapters.
@ -66,10 +67,11 @@ fun Manga.shouldDownloadNewChapters(db: DatabaseHelper, prefs: PreferencesHelper
if (includedCategories.isEmpty() && excludedCategories.isEmpty()) return true
// Get all categories, else default category (0)
val categoriesForManga =
db.getCategoriesForManga(this).executeAsBlocking()
val categoriesForManga = this.id?.let { mangaId ->
getCategories.awaitByMangaId(mangaId)
.mapNotNull { it.id }
.takeUnless { it.isEmpty() } ?: listOf(0)
.takeUnless { it.isEmpty() }
} ?: listOf(0)
if (categoriesForManga.any { it in excludedCategories }) return false
@ -88,9 +90,14 @@ fun Manga.moveCategories(
activity: Activity,
addingToLibrary: Boolean,
onMangaMoved: () -> Unit,
getCategories: GetCategories = Injekt.get(),
) {
val categories = db.getCategories().executeAsBlocking()
val categoriesForManga = db.getCategoriesForManga(this).executeAsBlocking()
// FIXME: Don't do blocking
val categories = runBlocking { getCategories.await() }
val categoriesForManga = runBlocking {
this@moveCategories.id?.let { mangaId -> getCategories.awaitByMangaId(mangaId) }
.orEmpty()
}
val ids = categoriesForManga.mapNotNull { it.id }.toTypedArray()
SetCategoriesSheet(
activity,
@ -110,15 +117,16 @@ fun List<Manga>.moveCategories(
db: DatabaseHelper,
activity: Activity,
onMangaMoved: () -> Unit,
getCategories: GetCategories = Injekt.get(),
) {
if (this.isEmpty()) return
val categories = db.getCategories().executeAsBlocking()
val commonCategories = map { db.getCategoriesForManga(it).executeAsBlocking() }
.reduce { set1: Iterable<Category>, set2 -> set1.intersect(set2).toMutableList() }
.toTypedArray()
val mangaCategories = map { db.getCategoriesForManga(it).executeAsBlocking() }
val common = mangaCategories.reduce { set1, set2 -> set1.intersect(set2).toMutableList() }
val mixedCategories = mangaCategories.flatten().distinct().subtract(common).toMutableList()
// FIXME: Don't do blocking
val categories = runBlocking { getCategories.await() }
val mangaCategories = map { manga ->
manga.id?.let { mangaId -> runBlocking { getCategories.awaitByMangaId(mangaId) } }.orEmpty()
}
val commonCategories = mangaCategories.reduce { set1, set2 -> set1.intersect(set2.toSet()).toMutableList() }.toSet()
val mixedCategories = mangaCategories.flatten().distinct().subtract(commonCategories).toMutableList()
SetCategoriesSheet(
activity,
this,
@ -147,7 +155,8 @@ fun Manga.addOrRemoveToFavorites(
onMangaAdded: (Pair<Long, Boolean>?) -> Unit,
onMangaMoved: () -> Unit,
onMangaDeleted: () -> Unit,
getManga: GetManga = Injekt.get()
getManga: GetManga = Injekt.get(),
getCategories: GetCategories = Injekt.get(),
): Snackbar? {
if (!favorite) {
if (checkForDupes) {
@ -187,7 +196,8 @@ fun Manga.addOrRemoveToFavorites(
}
}
val categories = db.getCategories().executeAsBlocking()
// FIXME: Don't do blocking
val categories = runBlocking { getCategories.await() }
val defaultCategoryId = preferences.defaultCategory().get()
val defaultCategory = categories.find { it.id == defaultCategoryId }
val lastUsedCategories = Category.lastCategoriesAddedTo.mapNotNull { catId ->