diff --git a/app/src/main/java/eu/kanade/domain/category/model/Category.kt b/app/src/main/java/eu/kanade/domain/category/model/Category.kt index f487555a3..6f7b0874b 100644 --- a/app/src/main/java/eu/kanade/domain/category/model/Category.kt +++ b/app/src/main/java/eu/kanade/domain/category/model/Category.kt @@ -1,6 +1,7 @@ package eu.kanade.domain.category.model import java.io.Serializable +import eu.kanade.tachiyomi.data.database.models.Category as DbCategory data class Category( val id: Long, @@ -11,3 +12,9 @@ data class Category( val mangaOrder: List, // SY <-- ) : Serializable + +fun Category.toDbCategory(): DbCategory = DbCategory.create(name).also { + it.id = id.toInt() + it.order = order.toInt() + it.flags = flags.toInt() +} diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/GetDuplicateLibraryManga.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/GetDuplicateLibraryManga.kt index 9a0b4a828..7193ed368 100644 --- a/app/src/main/java/eu/kanade/domain/manga/interactor/GetDuplicateLibraryManga.kt +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/GetDuplicateLibraryManga.kt @@ -3,7 +3,10 @@ package eu.kanade.domain.manga.interactor import eu.kanade.domain.manga.model.Manga import eu.kanade.domain.manga.repository.MangaRepository -class GetDuplicateLibraryManga(private val mangaRepository: MangaRepository) { +class GetDuplicateLibraryManga( + private val mangaRepository: MangaRepository, +) { + suspend fun await(title: String, sourceId: Long): Manga? { return mangaRepository.getDuplicateLibraryManga(title.lowercase(), sourceId) } diff --git a/app/src/main/java/eu/kanade/domain/manga/interactor/ResetViewerFlags.kt b/app/src/main/java/eu/kanade/domain/manga/interactor/ResetViewerFlags.kt index 13b766af1..e28d10ef7 100644 --- a/app/src/main/java/eu/kanade/domain/manga/interactor/ResetViewerFlags.kt +++ b/app/src/main/java/eu/kanade/domain/manga/interactor/ResetViewerFlags.kt @@ -5,6 +5,7 @@ import eu.kanade.domain.manga.repository.MangaRepository class ResetViewerFlags( private val mangaRepository: MangaRepository, ) { + suspend fun await(): Boolean { return mangaRepository.resetViewerFlags() } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt index 1600b7fa6..4ea5e154f 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourceController.kt @@ -20,6 +20,7 @@ import com.google.android.material.snackbar.Snackbar import dev.chrisbanes.insetter.applyInsetter import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.items.IFlexible +import eu.kanade.domain.category.model.toDbCategory import eu.kanade.domain.source.model.Source import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.database.models.Category @@ -815,42 +816,46 @@ open class BrowseSourceController(bundle: Bundle) : private fun addToLibrary(newManga: Manga, position: Int) { val activity = activity ?: return - val categories = presenter.getCategories() - val defaultCategoryId = preferences.defaultCategory() - val defaultCategory = categories.find { it.id == defaultCategoryId } + launchIO { + val categories = presenter.getCategories() + val defaultCategoryId = preferences.defaultCategory() + val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() } - when { - // Default category set - defaultCategory != null -> { - presenter.moveMangaToCategory(newManga, defaultCategory) + withUIContext { + when { + // Default category set + defaultCategory != null -> { + presenter.moveMangaToCategory(newManga, defaultCategory.toDbCategory()) - presenter.changeMangaFavorite(newManga) - adapter?.notifyItemChanged(position) - activity.toast(activity.getString(R.string.manga_added_library)) - } - - // Automatic 'Default' or no categories - defaultCategoryId == 0 || categories.isEmpty() -> { - presenter.moveMangaToCategory(newManga, null) - - presenter.changeMangaFavorite(newManga) - adapter?.notifyItemChanged(position) - activity.toast(activity.getString(R.string.manga_added_library)) - } - - // Choose a category - else -> { - val ids = presenter.getMangaCategoryIds(newManga) - val preselected = categories.map { - if (it.id in ids) { - QuadStateTextView.State.CHECKED.ordinal - } else { - QuadStateTextView.State.UNCHECKED.ordinal + presenter.changeMangaFavorite(newManga) + adapter?.notifyItemChanged(position) + activity.toast(activity.getString(R.string.manga_added_library)) } - }.toTypedArray() - ChangeMangaCategoriesDialog(this, listOf(newManga), categories, preselected) - .showDialog(router) + // Automatic 'Default' or no categories + defaultCategoryId == 0 || categories.isEmpty() -> { + presenter.moveMangaToCategory(newManga, null) + + presenter.changeMangaFavorite(newManga) + adapter?.notifyItemChanged(position) + activity.toast(activity.getString(R.string.manga_added_library)) + } + + // Choose a category + else -> { + val ids = presenter.getMangaCategoryIds(newManga) + val preselected = categories.map { + if (it.id in ids) { + QuadStateTextView.State.CHECKED.ordinal + } else { + QuadStateTextView.State.UNCHECKED.ordinal + } + }.toTypedArray() + + ChangeMangaCategoriesDialog(this@BrowseSourceController, listOf(newManga), categories.map { it.toDbCategory() }, preselected) + .showDialog(router) + } + } } } } diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt index cd94d3a62..d438af6d2 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/browse/source/browse/BrowseSourcePresenter.kt @@ -4,6 +4,7 @@ import android.os.Bundle import eu.davidea.flexibleadapter.items.IFlexible import eu.kanade.data.DatabaseHandler import eu.kanade.data.exh.savedSearchMapper +import eu.kanade.domain.category.interactor.GetCategories import eu.kanade.domain.manga.interactor.GetDuplicateLibraryManga import eu.kanade.domain.manga.model.toDbManga import eu.kanade.tachiyomi.data.cache.CoverCache @@ -55,6 +56,7 @@ import kotlinx.coroutines.flow.asFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -70,8 +72,8 @@ import rx.schedulers.Schedulers import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import xyz.nulldev.ts.api.http.serializer.FilterSerializer -import java.lang.RuntimeException import java.util.Date +import eu.kanade.domain.category.model.Category as DomainCategory open class BrowseSourcePresenter( private val sourceId: Long, @@ -86,6 +88,7 @@ open class BrowseSourcePresenter( private val prefs: PreferencesHelper = Injekt.get(), private val coverCache: CoverCache = Injekt.get(), private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(), + private val getCategories: GetCategories = Injekt.get(), ) : BasePresenter() { /** @@ -437,8 +440,8 @@ open class BrowseSourcePresenter( * * @return List of categories, not including the default category */ - fun getCategories(): List { - return db.getCategories().executeAsBlocking() + suspend fun getCategories(): List { + return getCategories.subscribe().firstOrNull() ?: emptyList() } suspend fun getDuplicateLibraryManga(manga: Manga): Manga? { @@ -451,9 +454,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 { + fun getMangaCategoryIds(manga: Manga): Array { val categories = db.getCategoriesForManga(manga).executeAsBlocking() - return categories.mapNotNull { it.id }.toTypedArray() + return categories.mapNotNull { it?.id?.toLong() }.toTypedArray() } /** diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt index e11fe4999..10924fc83 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaController.kt @@ -35,6 +35,7 @@ import dev.chrisbanes.insetter.applyInsetter import eu.davidea.flexibleadapter.FlexibleAdapter import eu.davidea.flexibleadapter.SelectableAdapter import eu.kanade.data.chapter.NoChaptersException +import eu.kanade.domain.category.model.toDbCategory import eu.kanade.domain.history.model.HistoryWithRelations import eu.kanade.tachiyomi.R import eu.kanade.tachiyomi.data.cache.CoverCache @@ -116,6 +117,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withContext import logcat.LogPriority import reactivecircus.flowbinding.recyclerview.scrollStateChanges @@ -478,20 +480,23 @@ class MangaController : } override fun onPrepareOptionsMenu(menu: Menu) { - // Hide options for local manga - menu.findItem(R.id.action_share).isVisible = !isLocalSource - menu.findItem(R.id.download_group).isVisible = !isLocalSource + runBlocking { + // Hide options for local manga + menu.findItem(R.id.action_share).isVisible = !isLocalSource + menu.findItem(R.id.download_group).isVisible = !isLocalSource - // Hide options for non-library manga - menu.findItem(R.id.action_edit_categories).isVisible = presenter.manga.favorite && presenter.getCategories().isNotEmpty() - menu.findItem(R.id.action_migrate).isVisible = presenter.manga.favorite /* SY --> */ && presenter.manga.source != MERGED_SOURCE_ID // SY <-- + // Hide options for non-library manga + menu.findItem(R.id.action_edit_categories).isVisible = + presenter.manga.favorite && presenter.getCategories().isNotEmpty() + menu.findItem(R.id.action_migrate).isVisible = presenter.manga.favorite - // SY --> - menu.findItem(R.id.action_edit).isVisible = presenter.manga.favorite || isLocalSource - menu.findItem(R.id.action_recommend).isVisible = preferences.recommendsInOverflow().get() - menu.findItem(R.id.action_merged).isVisible = presenter.manga.source == MERGED_SOURCE_ID - menu.findItem(R.id.action_toggle_dedupe).isVisible = false // presenter.manga.source == MERGED_SOURCE_ID - // SY <-- + // SY --> + menu.findItem(R.id.action_edit).isVisible = presenter.manga.favorite || isLocalSource + menu.findItem(R.id.action_recommend).isVisible = preferences.recommendsInOverflow().get() + menu.findItem(R.id.action_merged).isVisible = presenter.manga.source == MERGED_SOURCE_ID + menu.findItem(R.id.action_toggle_dedupe).isVisible = false // presenter.manga.source == MERGED_SOURCE_ID + // SY <-- + } } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -666,39 +671,47 @@ class MangaController : } private fun addToLibrary(newManga: Manga) { - val categories = presenter.getCategories() - val defaultCategoryId = preferences.defaultCategory() - val defaultCategory = categories.find { it.id == defaultCategoryId } + launchIO { + val categories = presenter.getCategories() + val defaultCategoryId = preferences.defaultCategory() + val defaultCategory = categories.find { it.id == defaultCategoryId.toLong() } - when { - // Default category set - defaultCategory != null -> { - toggleFavorite() - presenter.moveMangaToCategory(newManga, defaultCategory) - activity?.toast(activity?.getString(R.string.manga_added_library)) - activity?.invalidateOptionsMenu() - } - - // Automatic 'Default' or no categories - defaultCategoryId == 0 || categories.isEmpty() -> { - toggleFavorite() - presenter.moveMangaToCategory(newManga, null) - activity?.toast(activity?.getString(R.string.manga_added_library)) - activity?.invalidateOptionsMenu() - } - - // Choose a category - else -> { - val ids = presenter.getMangaCategoryIds(newManga) - val preselected = categories.map { - if (it.id in ids) { - QuadStateTextView.State.CHECKED.ordinal - } else { - QuadStateTextView.State.UNCHECKED.ordinal + withUIContext { + when { + // Default category set + defaultCategory != null -> { + toggleFavorite() + presenter.moveMangaToCategory(newManga, defaultCategory.toDbCategory()) + activity?.toast(activity?.getString(R.string.manga_added_library)) + activity?.invalidateOptionsMenu() } - }.toTypedArray() - showChangeCategoryDialog(newManga, categories, preselected) + // Automatic 'Default' or no categories + defaultCategoryId == 0 || categories.isEmpty() -> { + toggleFavorite() + presenter.moveMangaToCategory(newManga, null) + activity?.toast(activity?.getString(R.string.manga_added_library)) + activity?.invalidateOptionsMenu() + } + + // Choose a category + else -> { + val ids = presenter.getMangaCategoryIds(newManga) + val preselected = categories.map { + if (it.id in ids) { + QuadStateTextView.State.CHECKED.ordinal + } else { + QuadStateTextView.State.UNCHECKED.ordinal + } + }.toTypedArray() + + showChangeCategoryDialog( + newManga, + categories.map { it.toDbCategory() }, + preselected, + ) + } + } } } @@ -813,18 +826,27 @@ class MangaController : } fun onCategoriesClick() { - val manga = presenter.manga - val categories = presenter.getCategories() + launchIO { + val manga = presenter.manga + 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 + if (categories.isEmpty()) { + return@launchIO } - }.toTypedArray() - showChangeCategoryDialog(manga, categories, preselected) + + val ids = presenter.getMangaCategoryIds(manga) + val preselected = categories.map { + if (it.id in ids) { + QuadStateTextView.State.CHECKED.ordinal + } else { + QuadStateTextView.State.UNCHECKED.ordinal + } + }.toTypedArray() + + withUIContext { + showChangeCategoryDialog(manga, categories.map { it.toDbCategory() }, preselected) + } + } } private fun showChangeCategoryDialog(manga: Manga, categories: List, preselected: Array) { diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt index 7d2b96a12..daf22a763 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/MangaPresenter.kt @@ -4,6 +4,7 @@ import android.content.Context import android.net.Uri import android.os.Bundle import com.jakewharton.rxrelay.PublishRelay +import eu.kanade.domain.category.interactor.GetCategories import eu.kanade.domain.chapter.interactor.GetChapterByMangaId import eu.kanade.domain.chapter.interactor.GetMergedChapterByMangaId import eu.kanade.domain.chapter.model.toDbChapter @@ -76,6 +77,7 @@ import kotlinx.coroutines.awaitAll import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -91,6 +93,7 @@ import uy.kohesive.injekt.Injekt import uy.kohesive.injekt.api.get import uy.kohesive.injekt.injectLazy import java.util.Date +import eu.kanade.domain.category.model.Category as DomainCategory class MangaPresenter( val manga: Manga, @@ -103,6 +106,7 @@ class MangaPresenter( private val sourceManager: SourceManager = Injekt.get(), private val getChapterByMangaId: GetChapterByMangaId = Injekt.get(), private val getDuplicateLibraryManga: GetDuplicateLibraryManga = Injekt.get(), + private val getCategories: GetCategories = Injekt.get(), private val getMergedChapterByMangaId: GetMergedChapterByMangaId = Injekt.get(), ) : BasePresenter() { @@ -585,8 +589,8 @@ class MangaPresenter( * * @return List of categories, not including the default category */ - fun getCategories(): List { - return db.getCategories().executeAsBlocking() + suspend fun getCategories(): List { + return getCategories.subscribe().firstOrNull() ?: emptyList() } /** @@ -595,9 +599,9 @@ class MangaPresenter( * @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 { + fun getMangaCategoryIds(manga: Manga): Array { val categories = db.getCategoriesForManga(manga).executeAsBlocking() - return categories.mapNotNull { it.id }.toTypedArray() + return categories.mapNotNull { it?.id?.toLong() }.toTypedArray() } /** @@ -1041,14 +1045,14 @@ class MangaPresenter( /** * Whether downloaded only mode is enabled. */ - fun forceDownloaded(): Boolean { + private fun forceDownloaded(): Boolean { return manga.favorite && preferences.downloadedOnly().get() } /** * Whether the display only downloaded filter is enabled. */ - fun onlyDownloaded(): State { + private fun onlyDownloaded(): State { if (forceDownloaded()) { return State.INCLUDE } @@ -1062,7 +1066,7 @@ class MangaPresenter( /** * Whether the display only downloaded filter is enabled. */ - fun onlyBookmarked(): State { + private fun onlyBookmarked(): State { return when (manga.bookmarkedFilter) { Manga.CHAPTER_SHOW_BOOKMARKED -> State.INCLUDE Manga.CHAPTER_SHOW_NOT_BOOKMARKED -> State.EXCLUDE @@ -1073,7 +1077,7 @@ class MangaPresenter( /** * Whether the display only unread filter is enabled. */ - fun onlyUnread(): State { + private fun onlyUnread(): State { return when (manga.readFilter) { Manga.CHAPTER_SHOW_UNREAD -> State.INCLUDE Manga.CHAPTER_SHOW_READ -> State.EXCLUDE diff --git a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt index 80e4b4bb0..2eb7e0701 100644 --- a/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt +++ b/app/src/main/java/eu/kanade/tachiyomi/ui/manga/info/MangaInfoHeaderAdapter.kt @@ -136,7 +136,7 @@ class MangaInfoHeaderAdapter( .onEach { controller.onFavoriteClick() } .launchIn(controller.viewScope) - if (controller.presenter.manga.favorite && controller.presenter.getCategories().isNotEmpty()) { + if (controller.presenter.manga.favorite) { binding.btnFavorite.longClicks() .onEach { controller.onCategoriesClick() } .launchIn(controller.viewScope)