Browse Source

Use SQLDelight for a Category related queries (#7438)

Andreas 2 years ago
parent
commit
2674570792

+ 5 - 1
app/src/main/java/eu/kanade/data/category/CategoryRepositoryImpl.kt

@@ -11,7 +11,11 @@ class CategoryRepositoryImpl(
     private val handler: DatabaseHandler,
 ) : CategoryRepository {
 
-    override fun getAll(): Flow<List<Category>> {
+    override suspend fun getAll(): List<Category> {
+        return handler.awaitList { categoriesQueries.getCategories(categoryMapper) }
+    }
+
+    override fun getAllAsFlow(): Flow<List<Category>> {
         return handler.subscribeToList { categoriesQueries.getCategories(categoryMapper) }
     }
 

+ 5 - 1
app/src/main/java/eu/kanade/domain/category/interactor/GetCategories.kt

@@ -9,13 +9,17 @@ class GetCategories(
 ) {
 
     fun subscribe(): Flow<List<Category>> {
-        return categoryRepository.getAll()
+        return categoryRepository.getAllAsFlow()
     }
 
     fun subscribe(mangaId: Long): Flow<List<Category>> {
         return categoryRepository.getCategoriesByMangaIdAsFlow(mangaId)
     }
 
+    suspend fun await(): List<Category> {
+        return categoryRepository.getAll()
+    }
+
     suspend fun await(mangaId: Long): List<Category> {
         return categoryRepository.getCategoriesByMangaId(mangaId)
     }

+ 15 - 1
app/src/main/java/eu/kanade/domain/category/model/Category.kt

@@ -1,5 +1,7 @@
 package eu.kanade.domain.category.model
 
+import android.content.Context
+import eu.kanade.tachiyomi.R
 import java.io.Serializable
 import eu.kanade.tachiyomi.data.database.models.Category as DbCategory
 
@@ -8,7 +10,19 @@ data class Category(
     val name: String,
     val order: Long,
     val flags: Long,
-) : Serializable
+) : Serializable {
+
+    companion object {
+        val default = { context: Context ->
+            Category(
+                id = 0,
+                name = context.getString(R.string.default_category),
+                order = 0,
+                flags = 0,
+            )
+        }
+    }
+}
 
 fun Category.toDbCategory(): DbCategory = DbCategory.create(name).also {
     it.id = id.toInt()

+ 3 - 1
app/src/main/java/eu/kanade/domain/category/repository/CategoryRepository.kt

@@ -6,7 +6,9 @@ import kotlinx.coroutines.flow.Flow
 
 interface CategoryRepository {
 
-    fun getAll(): Flow<List<Category>>
+    suspend fun getAll(): List<Category>
+
+    fun getAllAsFlow(): Flow<List<Category>>
 
     suspend fun getCategoriesByMangaId(mangaId: Long): List<Category>
 

+ 1 - 2
app/src/main/java/eu/kanade/tachiyomi/data/database/DatabaseHelper.kt

@@ -10,7 +10,6 @@ import eu.kanade.tachiyomi.data.database.models.Category
 import eu.kanade.tachiyomi.data.database.models.Chapter
 import eu.kanade.tachiyomi.data.database.models.Manga
 import eu.kanade.tachiyomi.data.database.models.MangaCategory
-import eu.kanade.tachiyomi.data.database.queries.CategoryQueries
 import eu.kanade.tachiyomi.data.database.queries.ChapterQueries
 import eu.kanade.tachiyomi.data.database.queries.MangaCategoryQueries
 import eu.kanade.tachiyomi.data.database.queries.MangaQueries
@@ -21,7 +20,7 @@ import eu.kanade.tachiyomi.data.database.queries.MangaQueries
 class DatabaseHelper(
     openHelper: SupportSQLiteOpenHelper,
 ) :
-    MangaQueries, ChapterQueries, CategoryQueries, MangaCategoryQueries {
+    MangaQueries, ChapterQueries, MangaCategoryQueries {
 
     override val db = DefaultStorIOSQLite.builder()
         .sqliteOpenHelper(openHelper)

+ 0 - 30
app/src/main/java/eu/kanade/tachiyomi/data/database/queries/CategoryQueries.kt

@@ -1,30 +0,0 @@
-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
-
-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(mangaId: Long) = db.get()
-        .listOfObjects(Category::class.java)
-        .withQuery(
-            RawQuery.builder()
-                .query(getCategoriesForMangaQuery())
-                .args(mangaId)
-                .build(),
-        )
-        .prepare()
-}

+ 5 - 2
app/src/main/java/eu/kanade/tachiyomi/data/download/DownloadManager.kt

@@ -3,6 +3,7 @@ package eu.kanade.tachiyomi.data.download
 import android.content.Context
 import com.hippo.unifile.UniFile
 import com.jakewharton.rxrelay.BehaviorRelay
+import eu.kanade.domain.category.interactor.GetCategories
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.database.DatabaseHelper
 import eu.kanade.tachiyomi.data.database.models.Chapter
@@ -32,6 +33,7 @@ import uy.kohesive.injekt.injectLazy
 class DownloadManager(
     private val context: Context,
     private val db: DatabaseHelper = Injekt.get(),
+    private val getCategories: GetCategories = Injekt.get(),
 ) {
 
     private val sourceManager: SourceManager by injectLazy()
@@ -362,8 +364,9 @@ class DownloadManager(
     private fun getChaptersToDelete(chapters: List<Chapter>, manga: Manga): List<Chapter> {
         // Retrieve the categories that are set to exclude from being deleted on read
         val categoriesToExclude = preferences.removeExcludeCategories().get().map(String::toInt)
-        val categoriesForManga = db.getCategoriesForManga(manga.id!!).executeAsBlocking()
-            .mapNotNull { it.id }
+
+        val categoriesForManga = runBlocking { getCategories.await(manga.id!!) }
+            .map { it.id }
             .takeUnless { it.isEmpty() }
             ?: listOf(0)
 

+ 4 - 1
app/src/main/java/eu/kanade/tachiyomi/data/library/LibraryUpdateService.kt

@@ -7,6 +7,7 @@ import android.os.IBinder
 import android.os.PowerManager
 import androidx.core.content.ContextCompat
 import eu.kanade.data.chapter.NoChaptersException
+import eu.kanade.domain.category.interactor.GetCategories
 import eu.kanade.domain.chapter.interactor.GetChapterByMangaId
 import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
 import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
@@ -92,6 +93,7 @@ class LibraryUpdateService(
     private val getMangaById: GetMangaById = Injekt.get(),
     private val updateManga: UpdateManga = Injekt.get(),
     private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(),
+    private val getCategories: GetCategories = Injekt.get(),
     private val syncChaptersWithSource: SyncChaptersWithSource = Injekt.get(),
     private val getTracks: GetTracks = Injekt.get(),
     private val insertTrack: InsertTrack = Injekt.get(),
@@ -346,7 +348,8 @@ class LibraryUpdateService(
                                                     val newDbChapters = newChapters.map { it.toDbChapter() }
 
                                                     if (newChapters.isNotEmpty()) {
-                                                        if (mangaWithNotif.shouldDownloadNewChapters(db, preferences)) {
+                                                        val categoryIds = getCategories.await(domainManga.id).map { it.id }
+                                                        if (domainManga.shouldDownloadNewChapters(categoryIds, preferences)) {
                                                             downloadChapters(mangaWithNotif, newDbChapters)
                                                             hasDownloads.set(true)
                                                         }

+ 3 - 3
app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt

@@ -372,9 +372,9 @@ open class BrowseSourcePresenter(
      * @param manga the manga to get categories from.
      * @return Array of category ids the manga is in, if none returns default id
      */
-    fun getMangaCategoryIds(manga: Manga): Array<Long?> {
-        val categories = db.getCategoriesForManga(manga.id!!).executeAsBlocking()
-        return categories.mapNotNull { it?.id?.toLong() }.toTypedArray()
+    suspend fun getMangaCategoryIds(manga: Manga): Array<Long?> {
+        val categories = getCategories.await(manga.id!!)
+        return categories.map { it.id }.toTypedArray()
     }
 
     /**

+ 17 - 11
app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt

@@ -58,6 +58,8 @@ import eu.kanade.tachiyomi.ui.reader.ReaderActivity
 import eu.kanade.tachiyomi.ui.recent.history.HistoryController
 import eu.kanade.tachiyomi.ui.recent.updates.UpdatesController
 import eu.kanade.tachiyomi.ui.webview.WebViewActivity
+import eu.kanade.tachiyomi.util.lang.launchIO
+import eu.kanade.tachiyomi.util.lang.launchUI
 import eu.kanade.tachiyomi.util.system.logcat
 import eu.kanade.tachiyomi.util.system.toast
 import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
@@ -249,18 +251,22 @@ class MangaController :
     }
 
     private fun onCategoriesClick() {
-        val manga = presenter.manga ?: return
-        val categories = presenter.getCategories()
-
-        val ids = presenter.getMangaCategoryIds(manga)
-        val preselected = categories.map {
-            if (it.id in ids) {
-                QuadStateTextView.State.CHECKED.ordinal
-            } else {
-                QuadStateTextView.State.UNCHECKED.ordinal
+        viewScope.launchIO {
+            val manga = presenter.manga ?: return@launchIO
+            val categories = presenter.getCategories()
+
+            val ids = presenter.getMangaCategoryIds(manga)
+            val preselected = categories.map {
+                if (it.id in ids) {
+                    QuadStateTextView.State.CHECKED.ordinal
+                } else {
+                    QuadStateTextView.State.UNCHECKED.ordinal
+                }
+            }.toTypedArray()
+            launchUI {
+                showChangeCategoryDialog(manga.toDbManga(), categories, preselected)
             }
-        }.toTypedArray()
-        showChangeCategoryDialog(manga.toDbManga(), categories, preselected)
+        }
     }
 
     private fun showChangeCategoryDialog(manga: Manga, categories: List<Category>, preselected: Array<Int>) {

+ 9 - 7
app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt

@@ -4,6 +4,7 @@ import android.os.Bundle
 import androidx.compose.runtime.Immutable
 import eu.kanade.domain.category.interactor.GetCategories
 import eu.kanade.domain.category.interactor.SetMangaCategories
+import eu.kanade.domain.category.model.toDbCategory
 import eu.kanade.domain.chapter.interactor.SyncChaptersWithSource
 import eu.kanade.domain.chapter.interactor.SyncChaptersWithTrackServiceTwoWay
 import eu.kanade.domain.chapter.interactor.UpdateChapter
@@ -22,7 +23,6 @@ import eu.kanade.domain.track.interactor.GetTracks
 import eu.kanade.domain.track.interactor.InsertTrack
 import eu.kanade.domain.track.model.toDbTrack
 import eu.kanade.domain.track.model.toDomainTrack
-import eu.kanade.tachiyomi.data.database.DatabaseHelper
 import eu.kanade.tachiyomi.data.database.models.Category
 import eu.kanade.tachiyomi.data.database.models.Chapter
 import eu.kanade.tachiyomi.data.database.models.Manga
@@ -78,7 +78,6 @@ class MangaPresenter(
     val mangaId: Long,
     val isFromSource: Boolean,
     private val preferences: PreferencesHelper = Injekt.get(),
-    private val db: DatabaseHelper = Injekt.get(),
     private val trackManager: TrackManager = Injekt.get(),
     private val downloadManager: DownloadManager = Injekt.get(),
     private val getMangaAndChapters: GetMangaWithChapters = Injekt.get(),
@@ -326,8 +325,8 @@ class MangaPresenter(
      *
      * @return List of categories, not including the default category
      */
-    fun getCategories(): List<Category> {
-        return db.getCategories().executeAsBlocking()
+    suspend fun getCategories(): List<Category> {
+        return getCategories.await().map { it.toDbCategory() }
     }
 
     /**
@@ -597,9 +596,12 @@ class MangaPresenter(
     }
 
     private fun downloadNewChapters(chapters: List<Chapter>) {
-        val manga = successState?.manga ?: return
-        if (chapters.isEmpty() || !manga.shouldDownloadNewChapters(db, preferences)) return
-        downloadChapters(chapters.map { it.toDomainChapter()!! })
+        presenterScope.launchIO {
+            val manga = successState?.manga ?: return@launchIO
+            val categories = getCategories.await(manga.id).map { it.id }
+            if (chapters.isEmpty() || !manga.shouldDownloadNewChapters(categories, preferences)) return@launchIO
+            downloadChapters(chapters.map { it.toDomainChapter()!! })
+        }
     }
 
     /**

+ 12 - 11
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsDownloadController.kt

@@ -11,9 +11,9 @@ import androidx.core.text.buildSpannedString
 import androidx.preference.PreferenceScreen
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
 import com.hippo.unifile.UniFile
+import eu.kanade.domain.category.interactor.GetCategories
+import eu.kanade.domain.category.model.Category
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.data.database.DatabaseHelper
-import eu.kanade.tachiyomi.data.database.models.Category
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.ui.base.controller.DialogController
 import eu.kanade.tachiyomi.util.preference.bindTo
@@ -32,6 +32,7 @@ import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
 import eu.kanade.tachiyomi.widget.materialdialogs.setQuadStateMultiChoiceItems
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 import uy.kohesive.injekt.injectLazy
@@ -40,13 +41,13 @@ import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
 
 class SettingsDownloadController : SettingsController() {
 
-    private val db: DatabaseHelper by injectLazy()
+    private val getCategories: GetCategories by injectLazy()
 
     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
         titleRes = R.string.pref_category_downloads
 
-        val dbCategories = db.getCategories().executeAsBlocking()
-        val categories = listOf(Category.createDefault(context)) + dbCategories
+        val dbCategories = runBlocking { getCategories.await() }
+        val categories = listOf(Category.default(context)) + dbCategories
 
         preference {
             bindTo(preferences.downloadsDirectory())
@@ -116,7 +117,7 @@ class SettingsDownloadController : SettingsController() {
                 preferences.removeExcludeCategories().asFlow()
                     .onEach { mutable ->
                         val selected = mutable
-                            .mapNotNull { id -> categories.find { it.id == id.toInt() } }
+                            .mapNotNull { id -> categories.find { it.id == id.toLong() } }
                             .sortedBy { it.order }
 
                         summary = if (selected.isEmpty()) {
@@ -146,7 +147,7 @@ class SettingsDownloadController : SettingsController() {
 
                 fun updateSummary() {
                     val selectedCategories = preferences.downloadNewChapterCategories().get()
-                        .mapNotNull { id -> categories.find { it.id == id.toInt() } }
+                        .mapNotNull { id -> categories.find { it.id == id.toLong() } }
                         .sortedBy { it.order }
                     val includedItemsText = if (selectedCategories.isEmpty()) {
                         context.getString(R.string.all)
@@ -155,7 +156,7 @@ class SettingsDownloadController : SettingsController() {
                     }
 
                     val excludedCategories = preferences.downloadNewChapterCategoriesExclude().get()
-                        .mapNotNull { id -> categories.find { it.id == id.toInt() } }
+                        .mapNotNull { id -> categories.find { it.id == id.toLong() } }
                         .sortedBy { it.order }
                     val excludedItemsText = if (excludedCategories.isEmpty()) {
                         context.getString(R.string.none)
@@ -251,11 +252,11 @@ class SettingsDownloadController : SettingsController() {
     class DownloadCategoriesDialog : DialogController() {
 
         private val preferences: PreferencesHelper = Injekt.get()
-        private val db: DatabaseHelper = Injekt.get()
+        private val getCategories: GetCategories = Injekt.get()
 
         override fun onCreateDialog(savedViewState: Bundle?): Dialog {
-            val dbCategories = db.getCategories().executeAsBlocking()
-            val categories = listOf(Category.createDefault(activity!!)) + dbCategories
+            val dbCategories = runBlocking { getCategories.await() }
+            val categories = listOf(Category.default(activity!!)) + dbCategories
 
             val items = categories.map { it.name }
             var selected = categories

+ 13 - 12
app/src/main/java/eu/kanade/tachiyomi/ui/setting/SettingsLibraryController.kt

@@ -7,9 +7,9 @@ import androidx.core.content.ContextCompat
 import androidx.core.text.buildSpannedString
 import androidx.preference.PreferenceScreen
 import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import eu.kanade.domain.category.interactor.GetCategories
+import eu.kanade.domain.category.model.Category
 import eu.kanade.tachiyomi.R
-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
 import eu.kanade.tachiyomi.data.preference.DEVICE_CHARGING
@@ -41,6 +41,7 @@ import eu.kanade.tachiyomi.widget.materialdialogs.setQuadStateMultiChoiceItems
 import kotlinx.coroutines.flow.combine
 import kotlinx.coroutines.flow.launchIn
 import kotlinx.coroutines.flow.onEach
+import kotlinx.coroutines.runBlocking
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 import uy.kohesive.injekt.injectLazy
@@ -48,14 +49,14 @@ import eu.kanade.tachiyomi.data.preference.PreferenceKeys as Keys
 
 class SettingsLibraryController : SettingsController() {
 
-    private val db: DatabaseHelper = Injekt.get()
+    private val getCategories: GetCategories by injectLazy()
     private val trackManager: TrackManager by injectLazy()
 
     override fun setupPreferenceScreen(screen: PreferenceScreen) = screen.apply {
         titleRes = R.string.pref_category_library
 
-        val dbCategories = db.getCategories().executeAsBlocking()
-        val categories = listOf(Category.createDefault(context)) + dbCategories
+        val dbCategories = runBlocking { getCategories.await() }
+        val categories = listOf(Category.default(context)) + dbCategories
 
         preferenceCategory {
             titleRes = R.string.pref_category_display
@@ -110,12 +111,12 @@ class SettingsLibraryController : SettingsController() {
                 entryValues = arrayOf("-1") + categories.map { it.id.toString() }.toTypedArray()
                 defaultValue = "-1"
 
-                val selectedCategory = categories.find { it.id == preferences.defaultCategory() }
+                val selectedCategory = categories.find { it.id == preferences.defaultCategory().toLong() }
                 summary = selectedCategory?.name
                     ?: context.getString(R.string.default_category_summary)
                 onChange { newValue ->
                     summary = categories.find {
-                        it.id == (newValue as String).toInt()
+                        it.id == (newValue as String).toLong()
                     }?.name ?: context.getString(R.string.default_category_summary)
                     true
                 }
@@ -228,10 +229,10 @@ class SettingsLibraryController : SettingsController() {
 
                 fun updateSummary() {
                     val includedCategories = preferences.libraryUpdateCategories().get()
-                        .mapNotNull { id -> categories.find { it.id == id.toInt() } }
+                        .mapNotNull { id -> categories.find { it.id == id.toLong() } }
                         .sortedBy { it.order }
                     val excludedCategories = preferences.libraryUpdateCategoriesExclude().get()
-                        .mapNotNull { id -> categories.find { it.id == id.toInt() } }
+                        .mapNotNull { id -> categories.find { it.id == id.toLong() } }
                         .sortedBy { it.order }
 
                     val allExcluded = excludedCategories.size == categories.size
@@ -327,11 +328,11 @@ class SettingsLibraryController : SettingsController() {
     class LibraryGlobalUpdateCategoriesDialog : DialogController() {
 
         private val preferences: PreferencesHelper = Injekt.get()
-        private val db: DatabaseHelper = Injekt.get()
+        private val getCategories: GetCategories = Injekt.get()
 
         override fun onCreateDialog(savedViewState: Bundle?): Dialog {
-            val dbCategories = db.getCategories().executeAsBlocking()
-            val categories = listOf(Category.createDefault(activity!!)) + dbCategories
+            val dbCategories = runBlocking { getCategories.await() }
+            val categories = listOf(Category.default(activity!!)) + dbCategories
 
             val items = categories.map { it.name }
             var selected = categories

+ 5 - 17
app/src/main/java/eu/kanade/tachiyomi/util/MangaExtensions.kt

@@ -5,9 +5,7 @@ import eu.kanade.domain.manga.interactor.UpdateManga
 import eu.kanade.domain.manga.model.isLocal
 import eu.kanade.domain.manga.model.toDbManga
 import eu.kanade.tachiyomi.data.cache.CoverCache
-import eu.kanade.tachiyomi.data.database.DatabaseHelper
 import eu.kanade.tachiyomi.data.database.models.Manga
-import eu.kanade.tachiyomi.data.database.models.toDomainManga
 import eu.kanade.tachiyomi.data.preference.PreferencesHelper
 import eu.kanade.tachiyomi.source.LocalSource
 import eu.kanade.tachiyomi.source.model.SManga
@@ -56,37 +54,27 @@ fun Manga.removeCovers(coverCache: CoverCache = Injekt.get()): Int {
     return coverCache.deleteFromCache(this, true)
 }
 
-fun Manga.shouldDownloadNewChapters(db: DatabaseHelper, prefs: PreferencesHelper): Boolean {
-    return toDomainManga()?.shouldDownloadNewChapters(db, prefs) ?: false
-}
-
-fun DomainManga.shouldDownloadNewChapters(db: DatabaseHelper, prefs: PreferencesHelper): Boolean {
+fun DomainManga.shouldDownloadNewChapters(categories: List<Long>, prefs: PreferencesHelper): Boolean {
     if (!favorite) return false
 
     // Boolean to determine if user wants to automatically download new chapters.
     val downloadNewChapter = prefs.downloadNewChapter().get()
     if (!downloadNewChapter) return false
 
-    val includedCategories = prefs.downloadNewChapterCategories().get().map { it.toInt() }
-    val excludedCategories = prefs.downloadNewChapterCategoriesExclude().get().map { it.toInt() }
+    val includedCategories = prefs.downloadNewChapterCategories().get().map { it.toLong() }
+    val excludedCategories = prefs.downloadNewChapterCategoriesExclude().get().map { it.toLong() }
 
     // Default: Download from all categories
     if (includedCategories.isEmpty() && excludedCategories.isEmpty()) return true
 
-    // Get all categories, else default category (0)
-    val categoriesForManga =
-        db.getCategoriesForManga(id).executeAsBlocking()
-            .mapNotNull { it.id }
-            .takeUnless { it.isEmpty() } ?: listOf(0)
-
     // In excluded category
-    if (categoriesForManga.any { it in excludedCategories }) return false
+    if (categories.any { it in excludedCategories }) return false
 
     // Included category not selected
     if (includedCategories.isEmpty()) return true
 
     // In included category
-    return categoriesForManga.any { it in includedCategories }
+    return categories.any { it in includedCategories }
 }
 
 suspend fun DomainManga.editCover(