mirror of
https://github.com/null2264/yokai.git
synced 2025-06-21 02:34:39 +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.content.Context
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import eu.kanade.tachiyomi.data.backup.BackupNotifier
|
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.CategoriesBackupRestorer
|
||||||
import eu.kanade.tachiyomi.data.backup.restore.restorers.MangaBackupRestorer
|
import eu.kanade.tachiyomi.data.backup.restore.restorers.MangaBackupRestorer
|
||||||
import eu.kanade.tachiyomi.data.backup.restore.restorers.PreferenceBackupRestorer
|
import eu.kanade.tachiyomi.data.backup.restore.restorers.PreferenceBackupRestorer
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
package eu.kanade.tachiyomi.data.backup.restore.restorers
|
package eu.kanade.tachiyomi.data.backup.restore.restorers
|
||||||
|
|
||||||
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
import eu.kanade.tachiyomi.data.backup.models.BackupCategory
|
||||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
import yokai.data.DatabaseHandler
|
||||||
import yokai.domain.category.interactor.GetCategories
|
import yokai.domain.category.interactor.GetCategories
|
||||||
|
|
||||||
class CategoriesBackupRestorer(
|
class CategoriesBackupRestorer(
|
||||||
private val db: DatabaseHelper = Injekt.get(),
|
|
||||||
private val getCategories: GetCategories = Injekt.get(),
|
private val getCategories: GetCategories = Injekt.get(),
|
||||||
|
private val handler: DatabaseHandler = Injekt.get(),
|
||||||
) {
|
) {
|
||||||
suspend fun restoreCategories(backupCategories: List<BackupCategory>, onComplete: () -> Unit) {
|
suspend fun restoreCategories(backupCategories: List<BackupCategory>, onComplete: () -> Unit) {
|
||||||
// Get categories from file and from db
|
// Get categories from file and from db
|
||||||
// Do it outside of transaction because StorIO might hang because we're using SQLDelight
|
// Do it outside of transaction because StorIO might hang because we're using SQLDelight
|
||||||
val dbCategories = getCategories.await()
|
val dbCategories = getCategories.await()
|
||||||
db.inTransaction {
|
handler.await(true) {
|
||||||
// Iterate over them
|
// Iterate over them
|
||||||
backupCategories.map { it.getCategoryImpl() }.forEach { category ->
|
backupCategories.map { it.getCategoryImpl() }.forEach { category ->
|
||||||
// Used to know if the category is already in the db
|
// Used to know if the category is already in the db
|
||||||
|
@ -33,8 +33,13 @@ class CategoriesBackupRestorer(
|
||||||
if (!found) {
|
if (!found) {
|
||||||
// Let the db assign the id
|
// Let the db assign the id
|
||||||
category.id = null
|
category.id = null
|
||||||
val result = db.insertCategory(category).executeAsBlocking()
|
categoriesQueries.insert(
|
||||||
category.id = result.insertedId()?.toInt()
|
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
|
mangaSort = (LibrarySort.valueOf(sort) ?: LibrarySort.Title).categoryValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun mangaOrderToString(): String =
|
||||||
|
if (mangaSort != null) mangaSort.toString() else mangaOrder.joinToString("/")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var lastCategoriesAddedTo = emptySet<Int>()
|
var lastCategoriesAddedTo = emptySet<Int>()
|
||||||
|
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
package eu.kanade.tachiyomi.ui.category
|
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.data.database.models.Category
|
||||||
import eu.kanade.tachiyomi.ui.library.LibrarySort
|
import eu.kanade.tachiyomi.ui.library.LibrarySort
|
||||||
import eu.kanade.tachiyomi.util.system.executeOnIO
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import uy.kohesive.injekt.Injekt
|
|
||||||
import uy.kohesive.injekt.api.get
|
|
||||||
import uy.kohesive.injekt.injectLazy
|
import uy.kohesive.injekt.injectLazy
|
||||||
|
import yokai.domain.category.interactor.DeleteCategories
|
||||||
import yokai.domain.category.interactor.GetCategories
|
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.i18n.MR
|
||||||
import yokai.util.lang.getString
|
import yokai.util.lang.getString
|
||||||
|
|
||||||
|
@ -22,9 +22,11 @@ import yokai.util.lang.getString
|
||||||
*/
|
*/
|
||||||
class CategoryPresenter(
|
class CategoryPresenter(
|
||||||
private val controller: CategoryController,
|
private val controller: CategoryController,
|
||||||
private val db: DatabaseHelper = Injekt.get(),
|
|
||||||
) {
|
) {
|
||||||
|
private val deleteCategories: DeleteCategories by injectLazy()
|
||||||
private val getCategories: GetCategories 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)
|
private var scope = CoroutineScope(Job() + Dispatchers.Default)
|
||||||
|
|
||||||
|
@ -79,8 +81,8 @@ class CategoryPresenter(
|
||||||
|
|
||||||
// Insert into database.
|
// Insert into database.
|
||||||
cat.mangaSort = LibrarySort.Title.categoryValue
|
cat.mangaSort = LibrarySort.Title.categoryValue
|
||||||
db.insertCategory(cat).executeAsBlocking()
|
|
||||||
// FIXME: Don't do blocking
|
// FIXME: Don't do blocking
|
||||||
|
runBlocking { insertCategories.awaitOne(cat) }
|
||||||
val cats = runBlocking { getCategories.await() }
|
val cats = runBlocking { getCategories.await() }
|
||||||
val newCat = cats.find { it.name == name } ?: return false
|
val newCat = cats.find { it.name == name } ?: return false
|
||||||
categories.add(1, newCat)
|
categories.add(1, newCat)
|
||||||
|
@ -94,10 +96,12 @@ class CategoryPresenter(
|
||||||
* @param category The category to delete.
|
* @param category The category to delete.
|
||||||
*/
|
*/
|
||||||
fun deleteCategory(category: Category?) {
|
fun deleteCategory(category: Category?) {
|
||||||
val safeCategory = category ?: return
|
val safeCategory = category?.id ?: return
|
||||||
db.deleteCategory(safeCategory).executeAsBlocking()
|
scope.launch {
|
||||||
categories.remove(safeCategory)
|
deleteCategories.awaitOne(safeCategory.toLong())
|
||||||
controller.setCategories(categories.map(::CategoryItem))
|
categories.remove(category)
|
||||||
|
controller.setCategories(categories.map(::CategoryItem))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -107,10 +111,19 @@ class CategoryPresenter(
|
||||||
*/
|
*/
|
||||||
fun reorderCategories(categories: List<Category>) {
|
fun reorderCategories(categories: List<Category>) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
categories.forEachIndexed { i, category ->
|
val updates: MutableList<CategoryUpdate> = mutableListOf()
|
||||||
if (category.order != CREATE_CATEGORY_ORDER) category.order = i - 1
|
categories
|
||||||
}
|
.filter { it.order != CREATE_CATEGORY_ORDER }
|
||||||
db.insertCategories(categories.filter { it.order != CREATE_CATEGORY_ORDER }).executeOnIO()
|
.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()
|
this@CategoryPresenter.categories = categories.sortedBy { it.order }.toMutableList()
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
controller.setCategories(this@CategoryPresenter.categories.map(::CategoryItem))
|
controller.setCategories(this@CategoryPresenter.categories.map(::CategoryItem))
|
||||||
|
@ -135,7 +148,14 @@ class CategoryPresenter(
|
||||||
}
|
}
|
||||||
|
|
||||||
category.name = name
|
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
|
categories.find { it.id == category.id }?.name = name
|
||||||
controller.setCategories(categories.map(::CategoryItem))
|
controller.setCategories(categories.map(::CategoryItem))
|
||||||
return true
|
return true
|
||||||
|
|
|
@ -9,7 +9,10 @@ import yokai.data.library.custom.CustomMangaRepositoryImpl
|
||||||
import yokai.data.manga.MangaRepositoryImpl
|
import yokai.data.manga.MangaRepositoryImpl
|
||||||
import yokai.data.track.TrackRepositoryImpl
|
import yokai.data.track.TrackRepositoryImpl
|
||||||
import yokai.domain.category.CategoryRepository
|
import yokai.domain.category.CategoryRepository
|
||||||
|
import yokai.domain.category.interactor.DeleteCategories
|
||||||
import yokai.domain.category.interactor.GetCategories
|
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.ChapterRepository
|
||||||
import yokai.domain.chapter.interactor.DeleteChapter
|
import yokai.domain.chapter.interactor.DeleteChapter
|
||||||
import yokai.domain.chapter.interactor.GetAvailableScanlators
|
import yokai.domain.chapter.interactor.GetAvailableScanlators
|
||||||
|
@ -45,7 +48,10 @@ fun domainModule() = module {
|
||||||
factory { TrustExtension(get(), get()) }
|
factory { TrustExtension(get(), get()) }
|
||||||
|
|
||||||
single<CategoryRepository> { CategoryRepositoryImpl(get()) }
|
single<CategoryRepository> { CategoryRepositoryImpl(get()) }
|
||||||
|
factory { DeleteCategories(get()) }
|
||||||
factory { GetCategories(get()) }
|
factory { GetCategories(get()) }
|
||||||
|
factory { InsertCategories(get()) }
|
||||||
|
factory { UpdateCategories(get()) }
|
||||||
|
|
||||||
single<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
|
single<ExtensionRepoRepository> { ExtensionRepoRepositoryImpl(get()) }
|
||||||
factory { CreateExtensionRepo(get()) }
|
factory { CreateExtensionRepo(get()) }
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
package yokai.data.category
|
package yokai.data.category
|
||||||
|
|
||||||
|
import co.touchlab.kermit.Logger
|
||||||
import eu.kanade.tachiyomi.data.database.models.Category
|
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 kotlinx.coroutines.flow.Flow
|
||||||
import yokai.data.DatabaseHandler
|
import yokai.data.DatabaseHandler
|
||||||
|
import yokai.data.updateStrategyAdapter
|
||||||
import yokai.domain.category.CategoryRepository
|
import yokai.domain.category.CategoryRepository
|
||||||
|
import yokai.domain.category.models.CategoryUpdate
|
||||||
|
|
||||||
class CategoryRepositoryImpl(private val handler: DatabaseHandler) : CategoryRepository {
|
class CategoryRepositoryImpl(private val handler: DatabaseHandler) : CategoryRepository {
|
||||||
override suspend fun getAll(): List<Category> =
|
override suspend fun getAll(): List<Category> =
|
||||||
|
@ -14,4 +18,64 @@ class CategoryRepositoryImpl(private val handler: DatabaseHandler) : CategoryRep
|
||||||
|
|
||||||
override fun getAllAsFlow(): Flow<List<Category>> =
|
override fun getAllAsFlow(): Flow<List<Category>> =
|
||||||
handler.subscribeToList { categoriesQueries.findAll(Category::mapper) }
|
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 eu.kanade.tachiyomi.data.database.models.Category
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import yokai.domain.category.models.CategoryUpdate
|
||||||
|
|
||||||
interface CategoryRepository {
|
interface CategoryRepository {
|
||||||
suspend fun getAll(): List<Category>
|
suspend fun getAll(): List<Category>
|
||||||
suspend fun getAllByMangaId(mangaId: Long): List<Category>
|
suspend fun getAllByMangaId(mangaId: Long): List<Category>
|
||||||
fun getAllAsFlow(): Flow<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
|
SELECT categories.* FROM categories
|
||||||
JOIN mangas_categories ON categories._id = mangas_categories.category_id
|
JOIN mangas_categories ON categories._id = mangas_categories.category_id
|
||||||
WHERE mangas_categories.manga_id = :mangaId;
|
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