Move a few Dialogs to Compose (#7861)
* Move a few Dialogs to Compose - Separating dialogs that are not needed in the PR for the move to Compose on the Browse Source screen - ChangeMangaCategoriesDialog and AddDuplicateMangaDialog will be removed in the Browse Source screen PR * Review changes
This commit is contained in:
@@ -1,54 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.library
|
||||
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import com.bluelinelabs.conductor.Controller
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import eu.kanade.domain.manga.model.Manga
|
||||
import eu.kanade.domain.manga.model.isLocal
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
|
||||
class DeleteLibraryMangasDialog<T>(bundle: Bundle? = null) :
|
||||
DialogController(bundle) where T : Controller, T : DeleteLibraryMangasDialog.Listener {
|
||||
|
||||
private var mangas = emptyList<Manga>()
|
||||
|
||||
constructor(target: T, mangas: List<Manga>) : this() {
|
||||
this.mangas = mangas
|
||||
targetController = target
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
val canDeleteChapters = mangas.any { !it.isLocal() }
|
||||
val items = when (canDeleteChapters) {
|
||||
true -> listOf(
|
||||
R.string.manga_from_library,
|
||||
R.string.downloaded_chapters,
|
||||
)
|
||||
false -> listOf(R.string.manga_from_library)
|
||||
}
|
||||
.map { resources!!.getString(it) }
|
||||
.toTypedArray()
|
||||
|
||||
val selected = items
|
||||
.map { false }
|
||||
.toBooleanArray()
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setTitle(R.string.action_remove)
|
||||
.setMultiChoiceItems(items, selected) { _, which, checked ->
|
||||
selected[which] = checked
|
||||
}
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
val deleteFromLibrary = selected[0]
|
||||
val deleteChapters = canDeleteChapters && selected[1]
|
||||
(targetController as? Listener)?.deleteMangas(mangas, deleteFromLibrary, deleteChapters)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
|
||||
interface Listener {
|
||||
fun deleteMangas(mangas: List<Manga>, deleteFromLibrary: Boolean, deleteChapters: Boolean)
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,11 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.bluelinelabs.conductor.ControllerChangeHandler
|
||||
import com.bluelinelabs.conductor.ControllerChangeType
|
||||
import eu.kanade.domain.category.model.Category
|
||||
import eu.kanade.core.prefs.CheckboxState
|
||||
import eu.kanade.domain.manga.model.Manga
|
||||
import eu.kanade.domain.manga.model.isLocal
|
||||
import eu.kanade.domain.manga.model.toDbManga
|
||||
import eu.kanade.presentation.components.ChangeCategoryDialog
|
||||
import eu.kanade.presentation.library.LibraryScreen
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.data.database.models.toDomainManga
|
||||
@@ -19,20 +21,16 @@ import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.RootController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.pushController
|
||||
import eu.kanade.tachiyomi.ui.browse.source.globalsearch.GlobalSearchController
|
||||
import eu.kanade.tachiyomi.ui.category.CategoryController
|
||||
import eu.kanade.tachiyomi.ui.main.MainActivity
|
||||
import eu.kanade.tachiyomi.ui.manga.MangaController
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import eu.kanade.tachiyomi.widget.materialdialogs.QuadStateTextView
|
||||
import kotlinx.coroutines.cancel
|
||||
|
||||
class LibraryController(
|
||||
bundle: Bundle? = null,
|
||||
) : FullComposeController<LibraryPresenter>(bundle),
|
||||
RootController,
|
||||
ChangeMangaCategoriesDialog.Listener,
|
||||
DeleteLibraryMangasDialog.Listener {
|
||||
) : FullComposeController<LibraryPresenter>(bundle), RootController {
|
||||
|
||||
/**
|
||||
* Sheet containing filter/sort/display items.
|
||||
@@ -65,6 +63,36 @@ class LibraryController(
|
||||
onClickSelectAll = { presenter.selectAll(presenter.activeCategory) },
|
||||
onClickUnselectAll = ::clearSelection,
|
||||
)
|
||||
|
||||
val onDismissRequest = { presenter.dialog = null }
|
||||
when (val dialog = presenter.dialog) {
|
||||
is LibraryPresenter.Dialog.ChangeCategory -> {
|
||||
ChangeCategoryDialog(
|
||||
initialSelection = dialog.initialSelection,
|
||||
onDismissRequest = onDismissRequest,
|
||||
onEditCategories = {
|
||||
presenter.clearSelection()
|
||||
router.pushController(CategoryController())
|
||||
},
|
||||
onConfirm = { include, exclude ->
|
||||
presenter.clearSelection()
|
||||
presenter.setMangaCategories(dialog.manga, include, exclude)
|
||||
},
|
||||
)
|
||||
}
|
||||
is LibraryPresenter.Dialog.DeleteManga -> {
|
||||
DeleteLibraryMangaDialog(
|
||||
containsLocalManga = dialog.manga.any(Manga::isLocal),
|
||||
onDismissRequest = onDismissRequest,
|
||||
onConfirm = { deleteManga, deleteChapter ->
|
||||
presenter.removeMangas(dialog.manga.map { it.toDbManga() }, deleteManga, deleteChapter)
|
||||
presenter.clearSelection()
|
||||
},
|
||||
)
|
||||
}
|
||||
null -> {}
|
||||
}
|
||||
|
||||
LaunchedEffect(presenter.selectionMode) {
|
||||
val activity = (activity as? MainActivity) ?: return@LaunchedEffect
|
||||
// Could perhaps be removed when navigation is in a Compose world
|
||||
@@ -169,53 +197,40 @@ class LibraryController(
|
||||
private fun showMangaCategoriesDialog() {
|
||||
viewScope.launchIO {
|
||||
// Create a copy of selected manga
|
||||
val mangas = presenter.selection.toList()
|
||||
val mangaList = presenter.selection.mapNotNull { it.toDomainManga() }.toList()
|
||||
|
||||
// Hide the default category because it has a different behavior than the ones from db.
|
||||
val categories = presenter.categories.filter { it.id != 0L }
|
||||
|
||||
// Get indexes of the common categories to preselect.
|
||||
val common = presenter.getCommonCategories(mangas.mapNotNull { it.toDomainManga() })
|
||||
val common = presenter.getCommonCategories(mangaList)
|
||||
// Get indexes of the mix categories to preselect.
|
||||
val mix = presenter.getMixCategories(mangas.mapNotNull { it.toDomainManga() })
|
||||
val mix = presenter.getMixCategories(mangaList)
|
||||
val preselected = categories.map {
|
||||
when (it) {
|
||||
in common -> QuadStateTextView.State.CHECKED.ordinal
|
||||
in mix -> QuadStateTextView.State.INDETERMINATE.ordinal
|
||||
else -> QuadStateTextView.State.UNCHECKED.ordinal
|
||||
in common -> CheckboxState.State.Checked(it)
|
||||
in mix -> CheckboxState.TriState.Exclude(it)
|
||||
else -> CheckboxState.State.None(it)
|
||||
}
|
||||
}.toTypedArray()
|
||||
withUIContext {
|
||||
ChangeMangaCategoriesDialog(this@LibraryController, mangas.mapNotNull { it.toDomainManga() }, categories, preselected)
|
||||
.showDialog(router)
|
||||
}
|
||||
presenter.dialog = LibraryPresenter.Dialog.ChangeCategory(mangaList, preselected)
|
||||
}
|
||||
}
|
||||
|
||||
private fun downloadUnreadChapters() {
|
||||
val mangas = presenter.selection.toList()
|
||||
presenter.downloadUnreadChapters(mangas.mapNotNull { it.toDomainManga() })
|
||||
val mangaList = presenter.selection.toList()
|
||||
presenter.downloadUnreadChapters(mangaList.mapNotNull { it.toDomainManga() })
|
||||
presenter.clearSelection()
|
||||
}
|
||||
|
||||
private fun markReadStatus(read: Boolean) {
|
||||
val mangas = presenter.selection.toList()
|
||||
presenter.markReadStatus(mangas.mapNotNull { it.toDomainManga() }, read)
|
||||
val mangaList = presenter.selection.toList()
|
||||
presenter.markReadStatus(mangaList.mapNotNull { it.toDomainManga() }, read)
|
||||
presenter.clearSelection()
|
||||
}
|
||||
|
||||
private fun showDeleteMangaDialog() {
|
||||
val mangas = presenter.selection.toList()
|
||||
DeleteLibraryMangasDialog(this, mangas.mapNotNull { it.toDomainManga() }).showDialog(router)
|
||||
}
|
||||
|
||||
override fun updateCategoriesForMangas(mangas: List<Manga>, addCategories: List<Category>, removeCategories: List<Category>) {
|
||||
presenter.setMangaCategories(mangas, addCategories, removeCategories)
|
||||
presenter.clearSelection()
|
||||
}
|
||||
|
||||
override fun deleteMangas(mangas: List<Manga>, deleteFromLibrary: Boolean, deleteChapters: Boolean) {
|
||||
presenter.removeMangas(mangas.map { it.toDbManga() }, deleteFromLibrary, deleteChapters)
|
||||
presenter.clearSelection()
|
||||
val mangaList = presenter.selection.mapNotNull { it.toDomainManga() }.toList()
|
||||
presenter.dialog = LibraryPresenter.Dialog.DeleteManga(mangaList)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.util.fastAny
|
||||
import com.jakewharton.rxrelay.BehaviorRelay
|
||||
import eu.kanade.core.prefs.CheckboxState
|
||||
import eu.kanade.core.prefs.PreferenceMutableState
|
||||
import eu.kanade.core.util.asFlow
|
||||
import eu.kanade.core.util.asObservable
|
||||
@@ -610,13 +611,15 @@ class LibraryPresenter(
|
||||
* @param addCategories the categories to add for all mangas.
|
||||
* @param removeCategories the categories to remove in all mangas.
|
||||
*/
|
||||
fun setMangaCategories(mangaList: List<Manga>, addCategories: List<Category>, removeCategories: List<Category>) {
|
||||
fun setMangaCategories(mangaList: List<Manga>, addCategories: List<Long>, removeCategories: List<Long>) {
|
||||
presenterScope.launchIO {
|
||||
mangaList.map { manga ->
|
||||
val categoryIds = getCategories.await(manga.id)
|
||||
.map { it.id }
|
||||
.subtract(removeCategories)
|
||||
.plus(addCategories)
|
||||
.map { it.id }
|
||||
.toList()
|
||||
|
||||
setMangaCategories.await(manga.id, categoryIds)
|
||||
}
|
||||
}
|
||||
@@ -715,4 +718,9 @@ class LibraryPresenter(
|
||||
val items = (loadedManga[category.id] ?: emptyList()).map { it.manga }
|
||||
state.selection = items.filterNot { it in selection }
|
||||
}
|
||||
|
||||
sealed class Dialog {
|
||||
data class ChangeCategory(val manga: List<Manga>, val initialSelection: List<CheckboxState<Category>>) : Dialog()
|
||||
data class DeleteManga(val manga: List<Manga>) : Dialog()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user