mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-14 04:58:56 +01:00
Use Compose on Clear Database screen (#7639)
This commit is contained in:
@@ -1,171 +1,20 @@
|
||||
package eu.kanade.tachiyomi.ui.setting.database
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||
import dev.chrisbanes.insetter.applyInsetter
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.Payload
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.ClearDatabaseControllerBinding
|
||||
import eu.kanade.tachiyomi.ui.base.controller.DialogController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.FabController
|
||||
import eu.kanade.tachiyomi.ui.base.controller.NucleusController
|
||||
import eu.kanade.tachiyomi.util.system.toast
|
||||
import androidx.compose.runtime.Composable
|
||||
import eu.kanade.presentation.more.settings.database.ClearDatabaseScreen
|
||||
import eu.kanade.tachiyomi.ui.base.controller.FullComposeController
|
||||
|
||||
class ClearDatabaseController :
|
||||
NucleusController<ClearDatabaseControllerBinding, ClearDatabasePresenter>(),
|
||||
FlexibleAdapter.OnItemClickListener,
|
||||
FlexibleAdapter.OnUpdateListener,
|
||||
FabController {
|
||||
|
||||
private var recycler: RecyclerView? = null
|
||||
private var adapter: FlexibleAdapter<ClearDatabaseSourceItem>? = null
|
||||
|
||||
private var menu: Menu? = null
|
||||
|
||||
private var actionFab: ExtendedFloatingActionButton? = null
|
||||
|
||||
init {
|
||||
setHasOptionsMenu(true)
|
||||
}
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater): ClearDatabaseControllerBinding {
|
||||
return ClearDatabaseControllerBinding.inflate(inflater)
|
||||
}
|
||||
class ClearDatabaseController : FullComposeController<ClearDatabasePresenter>() {
|
||||
|
||||
override fun createPresenter(): ClearDatabasePresenter {
|
||||
return ClearDatabasePresenter()
|
||||
}
|
||||
|
||||
override fun getTitle(): String? {
|
||||
return activity?.getString(R.string.pref_clear_database)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View) {
|
||||
super.onViewCreated(view)
|
||||
|
||||
binding.recycler.applyInsetter {
|
||||
type(navigationBars = true) {
|
||||
padding()
|
||||
}
|
||||
}
|
||||
|
||||
adapter = FlexibleAdapter<ClearDatabaseSourceItem>(null, this, true)
|
||||
binding.recycler.adapter = adapter
|
||||
binding.recycler.layoutManager = LinearLayoutManager(view.context)
|
||||
binding.recycler.setHasFixedSize(true)
|
||||
adapter?.fastScroller = binding.fastScroller
|
||||
recycler = binding.recycler
|
||||
}
|
||||
|
||||
override fun onDestroyView(view: View) {
|
||||
adapter = null
|
||||
super.onDestroyView(view)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
inflater.inflate(R.menu.generic_selection, menu)
|
||||
this.menu = menu
|
||||
menu.forEach { menuItem -> menuItem.isVisible = (adapter?.itemCount ?: 0) > 0 }
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
val adapter = adapter ?: return false
|
||||
when (item.itemId) {
|
||||
R.id.action_select_all -> adapter.selectAll()
|
||||
R.id.action_select_inverse -> {
|
||||
adapter.currentItems.forEachIndexed { index, _ ->
|
||||
adapter.toggleSelection(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
updateFab()
|
||||
adapter.notifyItemRangeChanged(0, adapter.itemCount, Payload.SELECTION)
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onUpdateEmptyView(size: Int) {
|
||||
if (size > 0) {
|
||||
binding.emptyView.hide()
|
||||
} else {
|
||||
binding.emptyView.show(activity!!.getString(R.string.database_clean))
|
||||
}
|
||||
|
||||
menu?.forEach { menuItem -> menuItem.isVisible = size > 0 }
|
||||
}
|
||||
|
||||
override fun onItemClick(view: View?, position: Int): Boolean {
|
||||
val adapter = adapter ?: return false
|
||||
adapter.toggleSelection(position)
|
||||
adapter.notifyItemChanged(position, Payload.SELECTION)
|
||||
updateFab()
|
||||
return true
|
||||
}
|
||||
|
||||
fun setItems(items: List<ClearDatabaseSourceItem>) {
|
||||
adapter?.updateDataSet(items)
|
||||
}
|
||||
|
||||
override fun configureFab(fab: ExtendedFloatingActionButton) {
|
||||
fab.setIconResource(R.drawable.ic_delete_24dp)
|
||||
fab.setText(R.string.action_delete)
|
||||
fab.hide()
|
||||
fab.setOnClickListener {
|
||||
val ctrl = ClearDatabaseSourcesDialog()
|
||||
ctrl.targetController = this
|
||||
ctrl.showDialog(router)
|
||||
}
|
||||
actionFab = fab
|
||||
}
|
||||
|
||||
private fun updateFab() {
|
||||
val adapter = adapter ?: return
|
||||
if (adapter.selectedItemCount > 0) {
|
||||
actionFab?.show()
|
||||
} else {
|
||||
actionFab?.hide()
|
||||
}
|
||||
}
|
||||
|
||||
override fun cleanupFab(fab: ExtendedFloatingActionButton) {
|
||||
actionFab?.setOnClickListener(null)
|
||||
actionFab = null
|
||||
}
|
||||
|
||||
class ClearDatabaseSourcesDialog : DialogController() {
|
||||
override fun onCreateDialog(savedViewState: Bundle?): Dialog {
|
||||
return MaterialAlertDialogBuilder(activity!!)
|
||||
.setMessage(R.string.clear_database_confirmation)
|
||||
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||
(targetController as? ClearDatabaseController)?.clearDatabaseForSelectedSources()
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.create()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
private fun clearDatabaseForSelectedSources() {
|
||||
val adapter = adapter ?: return
|
||||
val selectedSourceIds = adapter.selectedPositions.mapNotNull { position ->
|
||||
adapter.getItem(position)?.source?.id
|
||||
}
|
||||
presenter.clearDatabaseForSourceIds(selectedSourceIds)
|
||||
actionFab!!.isVisible = false
|
||||
adapter.clearSelection()
|
||||
adapter.notifyDataSetChanged()
|
||||
activity?.toast(R.string.clear_database_completed)
|
||||
@Composable
|
||||
override fun ComposeContent() {
|
||||
ClearDatabaseScreen(
|
||||
presenter = presenter,
|
||||
navigateUp = { router.popCurrentController() },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,18 +2,21 @@ package eu.kanade.tachiyomi.ui.setting.database
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.kanade.domain.source.interactor.GetSourcesWithNonLibraryManga
|
||||
import eu.kanade.domain.source.model.Source
|
||||
import eu.kanade.presentation.more.settings.database.ClearDatabaseState
|
||||
import eu.kanade.presentation.more.settings.database.ClearDatabaseStateImpl
|
||||
import eu.kanade.tachiyomi.Database
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import uy.kohesive.injekt.Injekt
|
||||
import uy.kohesive.injekt.api.get
|
||||
|
||||
class ClearDatabasePresenter(
|
||||
private val state: ClearDatabaseStateImpl = ClearDatabaseState() as ClearDatabaseStateImpl,
|
||||
private val database: Database = Injekt.get(),
|
||||
private val getSourcesWithNonLibraryManga: GetSourcesWithNonLibraryManga = Injekt.get(),
|
||||
) : BasePresenter<ClearDatabaseController>() {
|
||||
) : BasePresenter<ClearDatabaseController>(), ClearDatabaseState by state {
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
@@ -21,17 +24,39 @@ class ClearDatabasePresenter(
|
||||
presenterScope.launchIO {
|
||||
getSourcesWithNonLibraryManga.subscribe()
|
||||
.collectLatest { list ->
|
||||
val items = list
|
||||
.map { (source, count) -> ClearDatabaseSourceItem(source, count) }
|
||||
.sortedBy { it.source.name }
|
||||
|
||||
withUIContext { view?.setItems(items) }
|
||||
state.items = list.sortedBy { it.name }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clearDatabaseForSourceIds(sources: List<Long>) {
|
||||
database.mangasQueries.deleteMangasNotInLibraryBySourceIds(sources)
|
||||
fun removeMangaBySourceId(sourceIds: List<Long>) {
|
||||
database.mangasQueries.deleteMangasNotInLibraryBySourceIds(sourceIds)
|
||||
database.historyQueries.removeResettedHistory()
|
||||
}
|
||||
|
||||
fun toggleSelection(source: Source) {
|
||||
val mutableList = state.selection.toMutableList()
|
||||
if (mutableList.contains(source.id)) {
|
||||
mutableList.remove(source.id)
|
||||
} else {
|
||||
mutableList.add(source.id)
|
||||
}
|
||||
state.selection = mutableList
|
||||
}
|
||||
|
||||
fun clearSelection() {
|
||||
state.selection = emptyList()
|
||||
}
|
||||
|
||||
fun selectAll() {
|
||||
state.selection = state.items.map { it.id }
|
||||
}
|
||||
|
||||
fun invertSelection() {
|
||||
state.selection = state.items.map { it.id }.filterNot { it in state.selection }
|
||||
}
|
||||
|
||||
sealed class Dialog {
|
||||
data class Delete(val sourceIds: List<Long>) : Dialog()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
package eu.kanade.tachiyomi.ui.setting.database
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import eu.davidea.flexibleadapter.FlexibleAdapter
|
||||
import eu.davidea.flexibleadapter.items.AbstractFlexibleItem
|
||||
import eu.davidea.flexibleadapter.items.IFlexible
|
||||
import eu.davidea.viewholders.FlexibleViewHolder
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.databinding.ClearDatabaseSourceItemBinding
|
||||
import eu.kanade.tachiyomi.source.LocalSource
|
||||
import eu.kanade.tachiyomi.source.Source
|
||||
import eu.kanade.tachiyomi.source.icon
|
||||
|
||||
data class ClearDatabaseSourceItem(val source: Source, private val mangaCount: Long) : AbstractFlexibleItem<ClearDatabaseSourceItem.Holder>() {
|
||||
|
||||
override fun getLayoutRes(): Int {
|
||||
return R.layout.clear_database_source_item
|
||||
}
|
||||
|
||||
override fun createViewHolder(view: View, adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>): Holder {
|
||||
return Holder(view, adapter)
|
||||
}
|
||||
|
||||
override fun bindViewHolder(adapter: FlexibleAdapter<IFlexible<RecyclerView.ViewHolder>>?, holder: Holder?, position: Int, payloads: MutableList<Any>?) {
|
||||
holder?.bind(source, mangaCount)
|
||||
}
|
||||
|
||||
class Holder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) {
|
||||
|
||||
private val binding = ClearDatabaseSourceItemBinding.bind(view)
|
||||
|
||||
fun bind(source: Source, count: Long) {
|
||||
binding.title.text = source.toString()
|
||||
binding.description.text = itemView.context.getString(R.string.clear_database_source_item_count, count)
|
||||
|
||||
itemView.post {
|
||||
when {
|
||||
source.icon() != null && source.id != LocalSource.ID ->
|
||||
binding.thumbnail.setImageDrawable(source.icon())
|
||||
else -> binding.thumbnail.setImageResource(R.mipmap.ic_local_source)
|
||||
}
|
||||
}
|
||||
|
||||
binding.checkbox.isChecked = (bindingAdapter as FlexibleAdapter<*>).isSelected(bindingAdapterPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user