mirror of
https://github.com/null2264/yokai.git
synced 2025-06-20 18:24:42 +00:00
refactor(db): Migrate some more category queries to SQLDelight
This commit is contained in:
parent
9c40aadca2
commit
ca41e02fe1
12 changed files with 187 additions and 21 deletions
|
@ -3,7 +3,6 @@ package eu.kanade.tachiyomi.data.backup.restore
|
|||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import eu.kanade.tachiyomi.data.backup.BackupNotifier
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupSource
|
||||
import eu.kanade.tachiyomi.data.backup.restore.restorers.CategoriesBackupRestorer
|
||||
import eu.kanade.tachiyomi.data.backup.restore.restorers.MangaBackupRestorer
|
||||
import eu.kanade.tachiyomi.data.backup.restore.restorers.PreferenceBackupRestorer
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
package eu.kanade.tachiyomi.data.backup.restore.restorers
|
||||
|
||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
import yokai.data.DatabaseHandler
|
||||
import yokai.domain.category.interactor.GetCategories
|
||||
|
||||
class CategoriesBackupRestorer(
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
private val getCategories: GetCategories = Injekt.get(),
|
||||
private val handler: DatabaseHandler = Injekt.get(),
|
||||
) {
|
||||
suspend fun restoreCategories(backupCategories: List<BackupCategory>, onComplete: () -> Unit) {
|
||||
// Get categories from file and from db
|
||||
// Do it outside of transaction because StorIO might hang because we're using SQLDelight
|
||||
val dbCategories = getCategories.await()
|
||||
db.inTransaction {
|
||||
handler.await(true) {
|
||||
// Iterate over them
|
||||
backupCategories.map { it.getCategoryImpl() }.forEach { category ->
|
||||
// Used to know if the category is already in the db
|
||||
|
@ -33,8 +33,13 @@ class CategoriesBackupRestorer(
|
|||
if (!found) {
|
||||
// Let the db assign the id
|
||||
category.id = null
|
||||
val result = db.insertCategory(category).executeAsBlocking()
|
||||
category.id = result.insertedId()?.toInt()
|
||||
categoriesQueries.insert(
|
||||
name = category.name,
|
||||
mangaOrder = category.mangaOrderToString(),
|
||||
sort = category.order.toLong(),
|
||||
flags = category.flags.toLong(),
|
||||
)
|
||||
category.id = categoriesQueries.selectLastInsertedRowId().executeAsOneOrNull()?.toInt()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,6 +53,9 @@ interface Category : Serializable {
|
|||
mangaSort = (LibrarySort.valueOf(sort) ?: LibrarySort.Title).categoryValue
|
||||
}
|
||||
|
||||
fun mangaOrderToString(): String =
|
||||
if (mangaSort != null) mangaSort.toString() else mangaOrder.joinToString("/")
|
||||
|
||||
companion object {
|
||||
var lastCategoriesAddedTo = emptySet<Int>()
|
||||
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
package eu.kanade.tachiyomi.ui.category
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.ui.library.LibrarySort
|
||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
||||
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.DeleteCategories
|
||||
import yokai.domain.category.interactor.GetCategories
|
||||
import yokai.domain.category.interactor.InsertCategories
|
||||
import yokai.domain.category.interactor.UpdateCategories
|
||||
import yokai.domain.category.models.CategoryUpdate
|
||||
import yokai.i18n.MR
|
||||
import yokai.util.lang.getString
|
||||
|
||||
|
@ -22,9 +22,11 @@ import yokai.util.lang.getString
|
|||
*/
|
||||
class CategoryPresenter(
|
||||
private val controller: CategoryController,
|
||||
private val db: DatabaseHelper = Injekt.get(),
|
||||
) {
|
||||
private val deleteCategories: DeleteCategories by injectLazy()
|
||||
private val getCategories: GetCategories by injectLazy()
|
||||
private val insertCategories: InsertCategories by injectLazy()
|
||||
private val updateCategories: UpdateCategories by injectLazy()
|
||||
|
||||
private var scope = CoroutineScope(Job() + Dispatchers.Default)
|
||||
|
||||
|
@ -79,8 +81,8 @@ class CategoryPresenter(
|
|||
|
||||
// Insert into database.
|
||||
cat.mangaSort = LibrarySort.Title.categoryValue
|
||||
db.insertCategory(cat).executeAsBlocking()
|
||||
// FIXME: Don't do blocking
|
||||
runBlocking { insertCategories.awaitOne(cat) }
|
||||
val cats = runBlocking { getCategories.await() }
|
||||
val newCat = cats.find { it.name == name } ?: return false
|
||||
categories.add(1, newCat)
|
||||
|
@ -94,10 +96,12 @@ class CategoryPresenter(
|
|||
* @param category The category to delete.
|
||||
*/
|
||||
fun deleteCategory(category: Category?) {
|
||||
val safeCategory = category ?: return
|
||||
db.deleteCategory(safeCategory).executeAsBlocking()
|
||||
categories.remove(safeCategory)
|
||||
controller.setCategories(categories.map(::CategoryItem))
|
||||
val safeCategory = category?.id ?: return
|
||||
scope.launch {
|
||||
deleteCategories.awaitOne(safeCategory.toLong())
|
||||
categories.remove(category)
|
||||
controller.setCategories(categories.map(::CategoryItem))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,10 +111,19 @@ class CategoryPresenter(
|
|||
*/
|
||||
fun reorderCategories(categories: List<Category>) {
|
||||
scope.launch {
|
||||
categories.forEachIndexed { i, category ->
|
||||
if (category.order != CREATE_CATEGORY_ORDER) category.order = i - 1
|
||||
}
|
||||
db.insertCategories(categories.filter { it.order != CREATE_CATEGORY_ORDER }).executeOnIO()
|
||||
val updates: MutableList<CategoryUpdate> = mutableListOf()
|
||||
categories
|
||||
.filter { it.order != CREATE_CATEGORY_ORDER }
|
||||
.forEachIndexed { i, category ->
|
||||
category.order = i - 1
|
||||
updates.add(
|
||||
CategoryUpdate(
|
||||
id = category.id!!.toLong(),
|
||||
order = category.order.toLong(),
|
||||
)
|
||||
)
|
||||
}
|
||||
updateCategories.await(updates)
|
||||
this@CategoryPresenter.categories = categories.sortedBy { it.order }.toMutableList()
|
||||
withContext(Dispatchers.Main) {
|
||||
controller.setCategories(this@CategoryPresenter.categories.map(::CategoryItem))
|
||||
|
@ -135,7 +148,14 @@ class CategoryPresenter(
|
|||
}
|
||||
|
||||
category.name = name
|
||||
db.insertCategory(category).executeAsBlocking()
|
||||
runBlocking {
|
||||
updateCategories.awaitOne(
|
||||
CategoryUpdate(
|
||||
id = category.id!!.toLong(),
|
||||
name = category.name,
|
||||
)
|
||||
)
|
||||
}
|
||||
categories.find { it.id == category.id }?.name = name
|
||||
controller.setCategories(categories.map(::CategoryItem))
|
||||
return true
|
||||
|
|
|
@ -9,7 +9,10 @@ import yokai.data.library.custom.CustomMangaRepositoryImpl
|
|||
import yokai.data.manga.MangaRepositoryImpl
|
||||
import yokai.data.track.TrackRepositoryImpl
|
||||
import yokai.domain.category.CategoryRepository
|
||||
import yokai.domain.category.interactor.DeleteCategories
|
||||
import yokai.domain.category.interactor.GetCategories
|
||||
import yokai.domain.category.interactor.InsertCategories
|
||||
import yokai.domain.category.interactor.UpdateCategories
|
||||
import yokai.domain.chapter.ChapterRepository
|
||||
import yokai.domain.chapter.interactor.DeleteChapter
|
||||
import yokai.domain.chapter.interactor.GetAvailableScanlators
|
||||
|
@ -45,7 +48,10 @@ fun domainModule() = module {
|
|||
factory { TrustExtension(get(), get()) }
|
||||
|
||||
single<CategoryRepository> { CategoryRepositoryImpl(get()) }
|
||||
factory { DeleteCategories(get()) }
|
||||
factory { GetCategories(get()) }
|
||||
factory { InsertCategories(get()) }
|
||||
factory { UpdateCategories(get()) }
|
||||
|
||||
single<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
|
||||
factory { CreateExtensionRepo(get()) }
|
||||
|
|
|
@ -1,9 +1,13 @@
|
|||
package yokai.data.category
|
||||
|
||||
import co.touchlab.kermit.Logger
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import eu.kanade.tachiyomi.data.database.tables.CategoryTable.COL_MANGA_ORDER
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import yokai.data.DatabaseHandler
|
||||
import yokai.data.updateStrategyAdapter
|
||||
import yokai.domain.category.CategoryRepository
|
||||
import yokai.domain.category.models.CategoryUpdate
|
||||
|
||||
class CategoryRepositoryImpl(private val handler: DatabaseHandler) : CategoryRepository {
|
||||
override suspend fun getAll(): List<Category> =
|
||||
|
@ -14,4 +18,64 @@ class CategoryRepositoryImpl(private val handler: DatabaseHandler) : CategoryRep
|
|||
|
||||
override fun getAllAsFlow(): Flow<List<Category>> =
|
||||
handler.subscribeToList { categoriesQueries.findAll(Category::mapper) }
|
||||
|
||||
override suspend fun insert(category: Category): Long? =
|
||||
handler.awaitOneOrNullExecutable {
|
||||
categoriesQueries.insert(
|
||||
name = category.name,
|
||||
mangaOrder = category.mangaOrderToString(),
|
||||
sort = category.order.toLong(),
|
||||
flags = category.flags.toLong(),
|
||||
)
|
||||
categoriesQueries.selectLastInsertedRowId()
|
||||
}
|
||||
|
||||
override suspend fun insertBulk(categories: List<Category>) =
|
||||
handler.await(true) {
|
||||
categories.forEach { category ->
|
||||
categoriesQueries.insert(
|
||||
name = category.name,
|
||||
mangaOrder = category.mangaOrderToString(),
|
||||
sort = category.order.toLong(),
|
||||
flags = category.flags.toLong(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun update(update: CategoryUpdate): Boolean {
|
||||
return try {
|
||||
partialUpdate(update)
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
Logger.e { "Failed to update category with id '${update.id}'" }
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateAll(updates: List<CategoryUpdate>): Boolean {
|
||||
return try {
|
||||
partialUpdate(*updates.toTypedArray())
|
||||
true
|
||||
} catch (e: Exception) {
|
||||
Logger.e(e) { "Failed to bulk update categories" }
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun partialUpdate(vararg updates: CategoryUpdate) {
|
||||
handler.await(inTransaction = true) {
|
||||
updates.forEach { update ->
|
||||
categoriesQueries.update(
|
||||
id = update.id,
|
||||
name = update.name,
|
||||
mangaOrder = update.mangaOrder,
|
||||
sort = update.order,
|
||||
flags = update.flags,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun delete(id: Long) =
|
||||
handler.await { categoriesQueries.delete(id) }
|
||||
}
|
||||
|
|
|
@ -2,9 +2,15 @@ package yokai.domain.category
|
|||
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import yokai.domain.category.models.CategoryUpdate
|
||||
|
||||
interface CategoryRepository {
|
||||
suspend fun getAll(): List<Category>
|
||||
suspend fun getAllByMangaId(mangaId: Long): List<Category>
|
||||
fun getAllAsFlow(): Flow<List<Category>>
|
||||
suspend fun insert(category: Category): Long?
|
||||
suspend fun insertBulk(categories: List<Category>)
|
||||
suspend fun update(update: CategoryUpdate): Boolean
|
||||
suspend fun updateAll(updates: List<CategoryUpdate>): Boolean
|
||||
suspend fun delete(id: Long)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package yokai.domain.category.interactor
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import yokai.domain.category.CategoryRepository
|
||||
import yokai.domain.category.models.CategoryUpdate
|
||||
|
||||
class DeleteCategories(
|
||||
private val categoryRepository: CategoryRepository,
|
||||
) {
|
||||
// suspend fun await(updates: List<Int>) =
|
||||
suspend fun awaitOne(id: Long) = categoryRepository.delete(id)
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package yokai.domain.category.interactor
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import yokai.domain.category.CategoryRepository
|
||||
|
||||
class InsertCategories(
|
||||
private val categoryRepository: CategoryRepository,
|
||||
) {
|
||||
suspend fun await(categories: List<Category>) = categoryRepository.insertBulk(categories)
|
||||
suspend fun awaitOne(category: Category) = categoryRepository.insert(category)
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package yokai.domain.category.interactor
|
||||
|
||||
import eu.kanade.tachiyomi.data.database.models.Category
|
||||
import yokai.domain.category.CategoryRepository
|
||||
import yokai.domain.category.models.CategoryUpdate
|
||||
|
||||
class UpdateCategories(
|
||||
private val categoryRepository: CategoryRepository,
|
||||
) {
|
||||
suspend fun await(updates: List<CategoryUpdate>) = categoryRepository.updateAll(updates)
|
||||
suspend fun awaitOne(update: CategoryUpdate) = categoryRepository.update(update)
|
||||
}
|
|
@ -15,3 +15,22 @@ findAllByMangaId:
|
|||
SELECT categories.* FROM categories
|
||||
JOIN mangas_categories ON categories._id = mangas_categories.category_id
|
||||
WHERE mangas_categories.manga_id = :mangaId;
|
||||
|
||||
insert:
|
||||
INSERT INTO categories (name, sort, flags, manga_order)
|
||||
VALUES (:name, :sort, :flags, :mangaOrder);
|
||||
|
||||
selectLastInsertedRowId:
|
||||
SELECT last_insert_rowid();
|
||||
|
||||
update:
|
||||
UPDATE categories SET
|
||||
name = coalesce(:name, name),
|
||||
sort = coalesce(:sort, sort),
|
||||
flags = coalesce(:flags, flags),
|
||||
manga_order = coalesce(:mangaOrder, manga_order)
|
||||
WHERE _id = :id;
|
||||
|
||||
delete:
|
||||
DELETE FROM categories
|
||||
WHERE _id = :id;
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
package yokai.domain.category.models
|
||||
|
||||
data class CategoryUpdate(
|
||||
val id: Long,
|
||||
val name: String? = null,
|
||||
val mangaOrder: String? = null,
|
||||
val order: Long? = null,
|
||||
val flags: Long? = null,
|
||||
)
|
Loading…
Add table
Add a link
Reference in a new issue