refactor(manga): Removing runBlocking

This commit is contained in:
Ahmad Ansori Palembani 2024-12-08 07:53:06 +07:00
parent f114320123
commit 2f8ae26a83
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
5 changed files with 233 additions and 209 deletions

View file

@ -101,6 +101,7 @@ import eu.kanade.tachiyomi.util.system.getResourceColor
import eu.kanade.tachiyomi.util.system.getResourceDrawable
import eu.kanade.tachiyomi.util.system.ignoredSystemInsets
import eu.kanade.tachiyomi.util.system.isImeVisible
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.system.materialAlertDialog
import eu.kanade.tachiyomi.util.system.openInBrowser
@ -128,7 +129,7 @@ import eu.kanade.tachiyomi.util.view.snack
import eu.kanade.tachiyomi.util.view.text
import eu.kanade.tachiyomi.util.view.withFadeTransaction
import eu.kanade.tachiyomi.widget.EmptyView
import java.util.*
import java.util.Locale
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.roundToInt
@ -2191,9 +2192,11 @@ open class LibraryController(
*/
private fun showChangeMangaCategoriesSheet() {
val activity = activity ?: return
selectedMangas.toList().moveCategories(activity) {
presenter.getLibrary()
destroyActionModeIfNeeded()
viewScope.launchIO {
selectedMangas.toList().moveCategories(activity) {
presenter.getLibrary()
destroyActionModeIfNeeded()
}
}
}

View file

@ -111,12 +111,14 @@ import eu.kanade.tachiyomi.util.system.isLandscape
import eu.kanade.tachiyomi.util.system.isOnline
import eu.kanade.tachiyomi.util.system.isPromptChecked
import eu.kanade.tachiyomi.util.system.isTablet
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.launchUI
import eu.kanade.tachiyomi.util.system.materialAlertDialog
import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
import eu.kanade.tachiyomi.util.system.setCustomTitleAndMessage
import eu.kanade.tachiyomi.util.system.timeSpanFromNow
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.withUIContext
import eu.kanade.tachiyomi.util.view.activityBinding
import eu.kanade.tachiyomi.util.view.copyToClipboard
import eu.kanade.tachiyomi.util.view.findChild
@ -1633,10 +1635,12 @@ class MangaDetailsController :
private fun showCategoriesSheet() {
val adding = !presenter.manga.favorite
presenter.manga.moveCategories(activity!!, adding) {
updateHeader()
if (adding) {
showAddedSnack()
viewScope.launchIO {
presenter.manga.moveCategories(activity!!, adding) {
updateHeader()
if (adding) {
showAddedSnack()
}
}
}
}
@ -1644,32 +1648,37 @@ class MangaDetailsController :
private fun toggleMangaFavorite() {
val view = view ?: return
val activity = activity ?: return
snack?.dismiss()
snack = presenter.manga.addOrRemoveToFavorites(
presenter.preferences,
view,
activity,
presenter.sourceManager,
this,
onMangaAdded = { migrationInfo ->
migrationInfo?.let {
viewScope.launchIO {
withUIContext { snack?.dismiss() }
snack = presenter.manga.addOrRemoveToFavorites(
presenter.preferences,
view,
activity,
presenter.sourceManager,
this@MangaDetailsController,
onMangaAdded = { migrationInfo ->
migrationInfo?.let {
presenter.fetchChapters(andTracking = true)
}
updateHeader()
showAddedSnack()
},
onMangaMoved = {
updateHeader()
presenter.fetchChapters(andTracking = true)
},
onMangaDeleted = {
updateHeader()
presenter.confirmDeletion()
},
scope = viewScope,
)
if (snack?.duration == Snackbar.LENGTH_INDEFINITE) {
withUIContext {
val favButton = getHeader()?.binding?.favoriteButton
(activity as? MainActivity)?.setUndoSnackBar(snack, favButton)
}
updateHeader()
showAddedSnack()
},
onMangaMoved = {
updateHeader()
presenter.fetchChapters(andTracking = true)
},
onMangaDeleted = {
updateHeader()
presenter.confirmDeletion()
},
)
if (snack?.duration == Snackbar.LENGTH_INDEFINITE) {
val favButton = getHeader()?.binding?.favoriteButton
(activity as? MainActivity)?.setUndoSnackBar(snack, favButton)
}
}
}

View file

@ -43,8 +43,10 @@ import eu.kanade.tachiyomi.util.addOrRemoveToFavorites
import eu.kanade.tachiyomi.util.system.connectivityManager
import eu.kanade.tachiyomi.util.system.dpToPx
import eu.kanade.tachiyomi.util.system.e
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.openInBrowser
import eu.kanade.tachiyomi.util.system.toast
import eu.kanade.tachiyomi.util.system.withUIContext
import eu.kanade.tachiyomi.util.view.activityBinding
import eu.kanade.tachiyomi.util.view.applyBottomAnimatedInsets
import eu.kanade.tachiyomi.util.view.fullAppBarHeight
@ -766,22 +768,27 @@ open class BrowseSourceController(bundle: Bundle) :
val manga = (adapter?.getItem(position) as? BrowseSourceItem?)?.manga ?: return
val view = view ?: return
val activity = activity ?: return
snack?.dismiss()
snack = manga.addOrRemoveToFavorites(
preferences,
view,
activity,
presenter.sourceManager,
this,
onMangaAdded = {
adapter?.notifyItemChanged(position)
snack = view.snack(MR.strings.added_to_library)
},
onMangaMoved = { adapter?.notifyItemChanged(position) },
onMangaDeleted = { presenter.confirmDeletion(manga) },
)
if (snack?.duration == Snackbar.LENGTH_INDEFINITE) {
(activity as? MainActivity)?.setUndoSnackBar(snack)
viewScope.launchIO {
withUIContext { snack?.dismiss() }
snack = manga.addOrRemoveToFavorites(
preferences,
view,
activity,
presenter.sourceManager,
this@BrowseSourceController,
onMangaAdded = {
adapter?.notifyItemChanged(position)
snack = view.snack(MR.strings.added_to_library)
},
onMangaMoved = { adapter?.notifyItemChanged(position) },
onMangaDeleted = { presenter.confirmDeletion(manga) },
scope = viewScope,
)
if (snack?.duration == Snackbar.LENGTH_INDEFINITE) {
withUIContext {
(activity as? MainActivity)?.setUndoSnackBar(snack)
}
}
}
}

View file

@ -24,7 +24,9 @@ import eu.kanade.tachiyomi.ui.manga.MangaDetailsController
import eu.kanade.tachiyomi.ui.source.browse.BrowseSourceController
import eu.kanade.tachiyomi.util.addOrRemoveToFavorites
import eu.kanade.tachiyomi.util.system.extensionIntentForText
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.rootWindowInsetsCompat
import eu.kanade.tachiyomi.util.system.withUIContext
import eu.kanade.tachiyomi.util.view.activityBinding
import eu.kanade.tachiyomi.util.view.isControllerVisible
import eu.kanade.tachiyomi.util.view.scrollViewWith
@ -114,34 +116,39 @@ open class GlobalSearchController(
val view = view ?: return
val activity = activity ?: return
snack?.dismiss()
snack = manga.addOrRemoveToFavorites(
preferences,
view,
activity,
presenter.sourceManager,
this,
onMangaAdded = { migrationInfo ->
migrationInfo?.let { (source, stillFaved) ->
val index = this.adapter
?.currentItems?.indexOfFirst { it.source.id == source } ?: return@let
val item = this.adapter?.getItem(index) ?: return@let
val oldMangaIndex = item.results?.indexOfFirst {
it.manga.title.lowercase() == manga.title.lowercase()
} ?: return@let
val oldMangaItem = item.results.getOrNull(oldMangaIndex)
oldMangaItem?.manga?.favorite = stillFaved
val holder = binding.recycler.findViewHolderForAdapterPosition(index) as? GlobalSearchHolder
holder?.updateManga(oldMangaIndex)
viewScope.launchIO {
withUIContext { snack?.dismiss() }
snack = manga.addOrRemoveToFavorites(
preferences,
view,
activity,
presenter.sourceManager,
this@GlobalSearchController,
onMangaAdded = { migrationInfo ->
migrationInfo?.let { (source, stillFaved) ->
val index = this@GlobalSearchController.adapter
?.currentItems?.indexOfFirst { it.source.id == source } ?: return@let
val item = this@GlobalSearchController.adapter?.getItem(index) ?: return@let
val oldMangaIndex = item.results?.indexOfFirst {
it.manga.title.lowercase() == manga.title.lowercase()
} ?: return@let
val oldMangaItem = item.results.getOrNull(oldMangaIndex)
oldMangaItem?.manga?.favorite = stillFaved
val holder = binding.recycler.findViewHolderForAdapterPosition(index) as? GlobalSearchHolder
holder?.updateManga(oldMangaIndex)
}
adapter.notifyItemChanged(position)
snack = view.snack(MR.strings.added_to_library)
},
onMangaMoved = { adapter.notifyItemChanged(position) },
onMangaDeleted = { presenter.confirmDeletion(manga) },
scope = viewScope,
)
if (snack?.duration == Snackbar.LENGTH_INDEFINITE) {
withUIContext {
(activity as? MainActivity)?.setUndoSnackBar(snack)
}
adapter.notifyItemChanged(position)
snack = view.snack(MR.strings.added_to_library)
},
onMangaMoved = { adapter.notifyItemChanged(position) },
onMangaDeleted = { presenter.confirmDeletion(manga) },
)
if (snack?.duration == Snackbar.LENGTH_INDEFINITE) {
(activity as? MainActivity)?.setUndoSnackBar(snack)
}
}
}

View file

@ -41,8 +41,9 @@ import eu.kanade.tachiyomi.util.view.withFadeTransaction
import eu.kanade.tachiyomi.widget.TriStateCheckBox
import java.util.Date
import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import yokai.domain.category.interactor.GetCategories
@ -84,69 +85,69 @@ suspend fun Manga.shouldDownloadNewChapters(prefs: PreferencesHelper, getCategor
return categoriesForManga.any { it in includedCategories }
}
fun Manga.moveCategories(activity: Activity, onMangaMoved: () -> Unit) {
suspend fun Manga.moveCategories(activity: Activity, onMangaMoved: () -> Unit) {
moveCategories(activity, false, onMangaMoved)
}
fun Manga.moveCategories(
suspend fun Manga.moveCategories(
activity: Activity,
addingToLibrary: Boolean,
onMangaMoved: () -> Unit,
) {
val getCategories: GetCategories = Injekt.get()
// FIXME: Don't do blocking
val categories = runBlocking { getCategories.await() }
val categoriesForManga = runBlocking {
this@moveCategories.id?.let { mangaId -> getCategories.awaitByMangaId(mangaId) }
.orEmpty()
}
val categories = getCategories.await()
val categoriesForManga = this.id?.let { mangaId -> getCategories.awaitByMangaId(mangaId) }.orEmpty()
val ids = categoriesForManga.mapNotNull { it.id }.toTypedArray()
SetCategoriesSheet(
activity,
this,
categories.toMutableList(),
ids,
addingToLibrary,
) {
onMangaMoved()
if (addingToLibrary) {
autoAddTrack(onMangaMoved)
}
}.show()
withUIContext {
SetCategoriesSheet(
activity,
this@moveCategories,
categories.toMutableList(),
ids,
addingToLibrary,
) {
onMangaMoved()
if (addingToLibrary) {
autoAddTrack(onMangaMoved)
}
}.show()
}
}
fun List<Manga>.moveCategories(
suspend fun List<Manga>.moveCategories(
activity: Activity,
onMangaMoved: () -> Unit,
) {
if (this.isEmpty()) return
val getCategories: GetCategories = Injekt.get()
// FIXME: Don't do blocking
val categories = runBlocking { getCategories.await() }
val categories = getCategories.await()
val mangaCategories = map { manga ->
manga.id?.let { mangaId -> runBlocking { getCategories.awaitByMangaId(mangaId) } }.orEmpty()
manga.id?.let { mangaId -> 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,
categories.toMutableList(),
categories.map {
when (it) {
in commonCategories -> TriStateCheckBox.State.CHECKED
in mixedCategories -> TriStateCheckBox.State.IGNORE
else -> TriStateCheckBox.State.UNCHECKED
}
}.toTypedArray(),
false,
) {
onMangaMoved()
}.show()
withUIContext {
SetCategoriesSheet(
activity,
this@moveCategories,
categories.toMutableList(),
categories.map {
when (it) {
in commonCategories -> TriStateCheckBox.State.CHECKED
in mixedCategories -> TriStateCheckBox.State.IGNORE
else -> TriStateCheckBox.State.UNCHECKED
}
}.toTypedArray(),
false,
) {
onMangaMoved()
}.show()
}
}
fun Manga.addOrRemoveToFavorites(
suspend fun Manga.addOrRemoveToFavorites(
preferences: PreferencesHelper,
view: View,
activity: Activity,
@ -160,15 +161,12 @@ fun Manga.addOrRemoveToFavorites(
setMangaCategories: SetMangaCategories = Injekt.get(),
getManga: GetManga = Injekt.get(),
updateManga: UpdateManga = Injekt.get(),
@OptIn(DelicateCoroutinesApi::class)
scope: CoroutineScope = GlobalScope,
): Snackbar? {
if (!favorite) {
if (checkForDupes) {
val duplicateManga = runBlocking(Dispatchers.IO) {
getManga.awaitDuplicateFavorite(
this@addOrRemoveToFavorites.title,
this@addOrRemoveToFavorites.source,
)
}
val duplicateManga = getManga.awaitDuplicateFavorite(this.title, this.source)
if (duplicateManga != null) {
showAddDuplicateDialog(
this,
@ -187,6 +185,7 @@ fun Manga.addOrRemoveToFavorites(
onMangaAdded,
onMangaMoved,
onMangaDeleted,
scope = scope,
)
},
migrateManga = { source, faved ->
@ -197,8 +196,7 @@ fun Manga.addOrRemoveToFavorites(
}
}
// FIXME: Don't do blocking
val categories = runBlocking { getCategories.await() }
val categories = getCategories.await()
val defaultCategoryId = preferences.defaultCategory().get()
val defaultCategory = categories.find { it.id == defaultCategoryId }
val lastUsedCategories = Category.lastCategoriesAddedTo.mapNotNull { catId ->
@ -209,22 +207,23 @@ fun Manga.addOrRemoveToFavorites(
favorite = true
date_added = Date().time
autoAddTrack(onMangaMoved)
// FIXME: Don't do blocking
runBlocking {
updateManga.await(
MangaUpdate(
id = this@addOrRemoveToFavorites.id!!,
favorite = true,
dateAdded = this@addOrRemoveToFavorites.date_added,
)
updateManga.await(
MangaUpdate(
id = this@addOrRemoveToFavorites.id!!,
favorite = true,
dateAdded = this@addOrRemoveToFavorites.date_added,
)
setMangaCategories.await(this@addOrRemoveToFavorites.id!!, listOf(defaultCategory.id!!.toLong()))
}
)
setMangaCategories.await(this@addOrRemoveToFavorites.id!!, listOf(defaultCategory.id!!.toLong()))
(activity as? MainActivity)?.showNotificationPermissionPrompt()
onMangaMoved()
return view.snack(activity.getString(MR.strings.added_to_, defaultCategory.name)) {
setAction(MR.strings.change) {
moveCategories(activity, onMangaMoved)
return withUIContext {
view.snack(activity.getString(MR.strings.added_to_, defaultCategory.name)) {
setAction(MR.strings.change) {
scope.launchIO {
moveCategories(activity, onMangaMoved)
}
}
}
}
}
@ -235,35 +234,36 @@ fun Manga.addOrRemoveToFavorites(
favorite = true
date_added = Date().time
autoAddTrack(onMangaMoved)
// FIXME: Don't do blocking
runBlocking {
updateManga.await(
MangaUpdate(
id = this@addOrRemoveToFavorites.id!!,
favorite = true,
dateAdded = this@addOrRemoveToFavorites.date_added,
)
updateManga.await(
MangaUpdate(
id = this@addOrRemoveToFavorites.id!!,
favorite = true,
dateAdded = this@addOrRemoveToFavorites.date_added,
)
setMangaCategories.await(this@addOrRemoveToFavorites.id!!, lastUsedCategories.map { it.id!!.toLong() })
}
)
setMangaCategories.await(this@addOrRemoveToFavorites.id!!, lastUsedCategories.map { it.id!!.toLong() })
(activity as? MainActivity)?.showNotificationPermissionPrompt()
onMangaMoved()
return view.snack(
activity.getString(
MR.strings.added_to_,
when (lastUsedCategories.size) {
0 -> activity.getString(MR.strings.default_category).lowercase(Locale.ROOT)
1 -> lastUsedCategories.firstOrNull()?.name ?: ""
else -> activity.getString(
MR.plurals.category_plural,
lastUsedCategories.size,
lastUsedCategories.size,
)
},
),
) {
setAction(MR.strings.change) {
moveCategories(activity, onMangaMoved)
return withUIContext {
view.snack(
activity.getString(
MR.strings.added_to_,
when (lastUsedCategories.size) {
0 -> activity.getString(MR.strings.default_category).lowercase(Locale.ROOT)
1 -> lastUsedCategories.firstOrNull()?.name ?: ""
else -> activity.getString(
MR.plurals.category_plural,
lastUsedCategories.size,
lastUsedCategories.size,
)
},
),
) {
setAction(MR.strings.change) {
scope.launchIO {
moveCategories(activity, onMangaMoved)
}
}
}
}
}
@ -271,27 +271,28 @@ fun Manga.addOrRemoveToFavorites(
favorite = true
date_added = Date().time
autoAddTrack(onMangaMoved)
// FIXME: Don't do blocking
runBlocking {
updateManga.await(
MangaUpdate(
id = this@addOrRemoveToFavorites.id!!,
favorite = true,
dateAdded = this@addOrRemoveToFavorites.date_added,
)
updateManga.await(
MangaUpdate(
id = this@addOrRemoveToFavorites.id!!,
favorite = true,
dateAdded = this@addOrRemoveToFavorites.date_added,
)
setMangaCategories.await(this@addOrRemoveToFavorites.id!!, emptyList())
}
)
setMangaCategories.await(this@addOrRemoveToFavorites.id!!, emptyList())
onMangaMoved()
(activity as? MainActivity)?.showNotificationPermissionPrompt()
return if (categories.isNotEmpty()) {
view.snack(activity.getString(MR.strings.added_to_, activity.getString(MR.strings.default_value))) {
setAction(MR.strings.change) {
moveCategories(activity, onMangaMoved)
return withUIContext {
if (categories.isNotEmpty()) {
view.snack(activity.getString(MR.strings.added_to_, activity.getString(MR.strings.default_value))) {
setAction(MR.strings.change) {
scope.launchIO {
moveCategories(activity, onMangaMoved)
}
}
}
} else {
view.snack(MR.strings.added_to_library)
}
} else {
view.snack(MR.strings.added_to_library)
}
}
else -> { // Always ask
@ -302,23 +303,19 @@ fun Manga.addOrRemoveToFavorites(
val lastAddedDate = date_added
favorite = false
date_added = 0
// FIXME: Don't do blocking
runBlocking {
updateManga.await(
MangaUpdate(
id = this@addOrRemoveToFavorites.id!!,
favorite = false,
dateAdded = 0,
)
updateManga.await(
MangaUpdate(
id = this@addOrRemoveToFavorites.id!!,
favorite = false,
dateAdded = 0,
)
}
)
onMangaMoved()
return view.snack(view.context.getString(MR.strings.removed_from_library), Snackbar.LENGTH_INDEFINITE) {
setAction(MR.strings.undo) {
favorite = true
date_added = lastAddedDate
// FIXME: Don't do blocking
runBlocking {
scope.launchIO {
updateManga.await(
MangaUpdate(
id = this@addOrRemoveToFavorites.id!!,
@ -344,39 +341,40 @@ fun Manga.addOrRemoveToFavorites(
return null
}
private fun Manga.showSetCategoriesSheet(
private suspend fun Manga.showSetCategoriesSheet(
activity: Activity,
categories: List<Category>,
onMangaAdded: (Pair<Long, Boolean>?) -> Unit,
onMangaMoved: () -> Unit,
getCategories: GetCategories = Injekt.get(),
) {
// FIXME: Don't do blocking
val categoriesForManga = runBlocking { getCategories.awaitByMangaId(this@showSetCategoriesSheet.id!!) }
val categoriesForManga = getCategories.awaitByMangaId(this.id!!)
val ids = categoriesForManga.mapNotNull { it.id }.toTypedArray()
SetCategoriesSheet(
activity,
this,
categories.toMutableList(),
ids,
true,
) {
(activity as? MainActivity)?.showNotificationPermissionPrompt()
onMangaAdded(null)
autoAddTrack(onMangaMoved)
}.show()
withUIContext {
SetCategoriesSheet(
activity,
this@showSetCategoriesSheet,
categories.toMutableList(),
ids,
true,
) {
(activity as? MainActivity)?.showNotificationPermissionPrompt()
onMangaAdded(null)
autoAddTrack(onMangaMoved)
}.show()
}
}
private fun showAddDuplicateDialog(
private suspend fun showAddDuplicateDialog(
newManga: Manga,
libraryManga: Manga,
activity: Activity,
sourceManager: SourceManager,
controller: Controller,
addManga: () -> Unit,
addManga: suspend () -> Unit,
migrateManga: (Long, Boolean) -> Unit,
) {
) = withUIContext {
val source = sourceManager.getOrStub(libraryManga.source)
val titles by lazy { MigrationFlags.titles(activity, libraryManga) }
@ -415,7 +413,7 @@ private fun showAddDuplicateDialog(
MangaDetailsController(libraryManga)
.withFadeTransaction(),
)
1 -> addManga()
1 -> launchIO { addManga() }
2 -> {
if (!newManga.initialized) {
activity.toast(MR.strings.must_view_details_before_migration, Toast.LENGTH_LONG)