Эх сурвалжийг харах

Use Compose on Clear Database screen (#7639)

Andreas 2 жил өмнө
parent
commit
99ac30e59f
16 өөрчлөгдсөн 354 нэмэгдсэн , 325 устгасан
  1. 3 3
      app/src/main/java/eu/kanade/data/source/SourceRepositoryImpl.kt
  2. 2 2
      app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithNonLibraryManga.kt
  3. 13 0
      app/src/main/java/eu/kanade/domain/source/model/SourceWithCount.kt
  4. 2 2
      app/src/main/java/eu/kanade/domain/source/repository/SourceRepository.kt
  5. 57 0
      app/src/main/java/eu/kanade/presentation/more/settings/database/ClearDatabaseScreen.kt
  6. 28 0
      app/src/main/java/eu/kanade/presentation/more/settings/database/ClearDatabaseState.kt
  7. 41 0
      app/src/main/java/eu/kanade/presentation/more/settings/database/components/ClearDatabaseContent.kt
  8. 31 0
      app/src/main/java/eu/kanade/presentation/more/settings/database/components/ClearDatabaseDialogs.kt
  9. 38 0
      app/src/main/java/eu/kanade/presentation/more/settings/database/components/ClearDatabaseFloatingActionButton.kt
  10. 53 0
      app/src/main/java/eu/kanade/presentation/more/settings/database/components/ClearDatabaseItem.kt
  11. 42 0
      app/src/main/java/eu/kanade/presentation/more/settings/database/components/ClearDatabaseToolbar.kt
  12. 10 161
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/database/ClearDatabaseController.kt
  13. 34 9
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/database/ClearDatabasePresenter.kt
  14. 0 48
      app/src/main/java/eu/kanade/tachiyomi/ui/setting/database/ClearDatabaseSourceItem.kt
  15. 0 32
      app/src/main/res/layout/clear_database_controller.xml
  16. 0 68
      app/src/main/res/layout/clear_database_source_item.xml

+ 3 - 3
app/src/main/java/eu/kanade/data/source/SourceRepositoryImpl.kt

@@ -2,12 +2,12 @@ package eu.kanade.data.source
 
 import eu.kanade.data.DatabaseHandler
 import eu.kanade.domain.source.model.Source
+import eu.kanade.domain.source.model.SourceWithCount
 import eu.kanade.domain.source.repository.SourceRepository
 import eu.kanade.tachiyomi.source.LocalSource
 import eu.kanade.tachiyomi.source.SourceManager
 import kotlinx.coroutines.flow.Flow
 import kotlinx.coroutines.flow.map
-import eu.kanade.tachiyomi.source.Source as LoadedSource
 
 class SourceRepositoryImpl(
     private val sourceManager: SourceManager,
@@ -40,12 +40,12 @@ class SourceRepositoryImpl(
         }
     }
 
-    override fun getSourcesWithNonLibraryManga(): Flow<List<Pair<LoadedSource, Long>>> {
+    override fun getSourcesWithNonLibraryManga(): Flow<List<SourceWithCount>> {
         val sourceIdWithNonLibraryManga = handler.subscribeToList { mangasQueries.getSourceIdsWithNonLibraryManga() }
         return sourceIdWithNonLibraryManga.map { sourceId ->
             sourceId.map { (sourceId, count) ->
                 val source = sourceManager.getOrStub(sourceId)
-                source to count
+                SourceWithCount(sourceMapper(source), count)
             }
         }
     }

+ 2 - 2
app/src/main/java/eu/kanade/domain/source/interactor/GetSourcesWithNonLibraryManga.kt

@@ -1,14 +1,14 @@
 package eu.kanade.domain.source.interactor
 
+import eu.kanade.domain.source.model.SourceWithCount
 import eu.kanade.domain.source.repository.SourceRepository
-import eu.kanade.tachiyomi.source.Source
 import kotlinx.coroutines.flow.Flow
 
 class GetSourcesWithNonLibraryManga(
     private val repository: SourceRepository,
 ) {
 
-    fun subscribe(): Flow<List<Pair<Source, Long>>> {
+    fun subscribe(): Flow<List<SourceWithCount>> {
         return repository.getSourcesWithNonLibraryManga()
     }
 }

+ 13 - 0
app/src/main/java/eu/kanade/domain/source/model/SourceWithCount.kt

@@ -0,0 +1,13 @@
+package eu.kanade.domain.source.model
+
+data class SourceWithCount(
+    val source: Source,
+    val count: Long,
+) {
+
+    val id: Long
+        get() = source.id
+
+    val name: String
+        get() = source.name
+}

+ 2 - 2
app/src/main/java/eu/kanade/domain/source/repository/SourceRepository.kt

@@ -1,8 +1,8 @@
 package eu.kanade.domain.source.repository
 
 import eu.kanade.domain.source.model.Source
+import eu.kanade.domain.source.model.SourceWithCount
 import kotlinx.coroutines.flow.Flow
-import eu.kanade.tachiyomi.source.Source as LoadedSource
 
 interface SourceRepository {
 
@@ -12,5 +12,5 @@ interface SourceRepository {
 
     fun getSourcesWithFavoriteCount(): Flow<List<Pair<Source, Long>>>
 
-    fun getSourcesWithNonLibraryManga(): Flow<List<Pair<LoadedSource, Long>>>
+    fun getSourcesWithNonLibraryManga(): Flow<List<SourceWithCount>>
 }

+ 57 - 0
app/src/main/java/eu/kanade/presentation/more/settings/database/ClearDatabaseScreen.kt

@@ -0,0 +1,57 @@
+package eu.kanade.presentation.more.settings.database
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.platform.LocalContext
+import eu.kanade.presentation.components.Scaffold
+import eu.kanade.presentation.more.settings.database.components.ClearDatabaseContent
+import eu.kanade.presentation.more.settings.database.components.ClearDatabaseDeleteDialog
+import eu.kanade.presentation.more.settings.database.components.ClearDatabaseFloatingActionButton
+import eu.kanade.presentation.more.settings.database.components.ClearDatabaseToolbar
+import eu.kanade.presentation.util.plus
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.ui.setting.database.ClearDatabasePresenter
+import eu.kanade.tachiyomi.util.system.toast
+
+@Composable
+fun ClearDatabaseScreen(
+    presenter: ClearDatabasePresenter,
+    navigateUp: () -> Unit,
+) {
+    val context = LocalContext.current
+    Scaffold(
+        topBar = {
+            ClearDatabaseToolbar(
+                state = presenter,
+                navigateUp = navigateUp,
+                onClickSelectAll = { presenter.selectAll() },
+                onClickInvertSelection = { presenter.invertSelection() },
+            )
+        },
+        floatingActionButton = {
+            ClearDatabaseFloatingActionButton(
+                isVisible = presenter.selection.isNotEmpty(),
+                onClickDelete = {
+                    presenter.dialog = ClearDatabasePresenter.Dialog.Delete(presenter.selection)
+                },
+            )
+        },
+    ) { paddingValues ->
+        ClearDatabaseContent(
+            state = presenter,
+            contentPadding = paddingValues,
+            onClickSelection = { source ->
+                presenter.toggleSelection(source)
+            },
+        )
+    }
+    if (presenter.dialog is ClearDatabasePresenter.Dialog.Delete) {
+        ClearDatabaseDeleteDialog(
+            onDismissRequest = { presenter.dialog = null },
+            onDelete = {
+                presenter.removeMangaBySourceId((presenter.dialog as ClearDatabasePresenter.Dialog.Delete).sourceIds)
+                presenter.clearSelection()
+                context.toast(R.string.clear_database_completed)
+            },
+        )
+    }
+}

+ 28 - 0
app/src/main/java/eu/kanade/presentation/more/settings/database/ClearDatabaseState.kt

@@ -0,0 +1,28 @@
+package eu.kanade.presentation.more.settings.database
+
+import androidx.compose.runtime.Stable
+import androidx.compose.runtime.derivedStateOf
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import eu.kanade.domain.source.model.SourceWithCount
+import eu.kanade.tachiyomi.ui.setting.database.ClearDatabasePresenter
+
+@Stable
+interface ClearDatabaseState {
+    val items: List<SourceWithCount>
+    val selection: List<Long>
+    val isEmpty: Boolean
+    var dialog: ClearDatabasePresenter.Dialog?
+}
+
+fun ClearDatabaseState(): ClearDatabaseState {
+    return ClearDatabaseStateImpl()
+}
+
+class ClearDatabaseStateImpl : ClearDatabaseState {
+    override var items: List<SourceWithCount> by mutableStateOf(emptyList())
+    override var selection: List<Long> by mutableStateOf(emptyList())
+    override val isEmpty: Boolean by derivedStateOf { items.isEmpty() }
+    override var dialog: ClearDatabasePresenter.Dialog? by mutableStateOf(null)
+}

+ 41 - 0
app/src/main/java/eu/kanade/presentation/more/settings/database/components/ClearDatabaseContent.kt

@@ -0,0 +1,41 @@
+package eu.kanade.presentation.more.settings.database.components
+
+import androidx.compose.animation.Crossfade
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.WindowInsets
+import androidx.compose.foundation.layout.asPaddingValues
+import androidx.compose.foundation.layout.navigationBars
+import androidx.compose.foundation.lazy.items
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import eu.kanade.domain.source.model.Source
+import eu.kanade.presentation.components.EmptyScreen
+import eu.kanade.presentation.components.FastScrollLazyColumn
+import eu.kanade.presentation.more.settings.database.ClearDatabaseState
+import eu.kanade.presentation.util.plus
+import eu.kanade.tachiyomi.R
+
+@Composable
+fun ClearDatabaseContent(
+    state: ClearDatabaseState,
+    contentPadding: PaddingValues,
+    onClickSelection: (Source) -> Unit,
+) {
+    Crossfade(targetState = state.isEmpty.not()) { _state ->
+        when (_state) {
+            true -> FastScrollLazyColumn(
+                contentPadding = contentPadding + WindowInsets.navigationBars.asPaddingValues(),
+            ) {
+                items(state.items) { sourceWithCount ->
+                    ClearDatabaseItem(
+                        source = sourceWithCount.source,
+                        count = sourceWithCount.count,
+                        isSelected = state.selection.contains(sourceWithCount.id),
+                        onClickSelect = { onClickSelection(sourceWithCount.source) },
+                    )
+                }
+            }
+            false -> EmptyScreen(message = stringResource(id = R.string.database_clean))
+        }
+    }
+}

+ 31 - 0
app/src/main/java/eu/kanade/presentation/more/settings/database/components/ClearDatabaseDialogs.kt

@@ -0,0 +1,31 @@
+package eu.kanade.presentation.more.settings.database.components
+
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import eu.kanade.presentation.components.TextButton
+import eu.kanade.tachiyomi.R
+
+@Composable
+fun ClearDatabaseDeleteDialog(
+    onDismissRequest: () -> Unit,
+    onDelete: () -> Unit,
+) {
+    AlertDialog(
+        onDismissRequest = onDismissRequest,
+        confirmButton = {
+            TextButton(onClick = onDelete) {
+                Text(text = stringResource(id = android.R.string.ok))
+            }
+        },
+        dismissButton = {
+            TextButton(onClick = onDismissRequest) {
+                Text(text = stringResource(id = android.R.string.cancel))
+            }
+        },
+        text = {
+            Text(text = stringResource(id = R.string.clear_database_confirmation))
+        },
+    )
+}

+ 38 - 0
app/src/main/java/eu/kanade/presentation/more/settings/database/components/ClearDatabaseFloatingActionButton.kt

@@ -0,0 +1,38 @@
+package eu.kanade.presentation.more.settings.database.components
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.animation.fadeIn
+import androidx.compose.animation.fadeOut
+import androidx.compose.foundation.layout.navigationBarsPadding
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import eu.kanade.presentation.components.ExtendedFloatingActionButton
+import eu.kanade.tachiyomi.R
+
+@Composable
+fun ClearDatabaseFloatingActionButton(
+    isVisible: Boolean,
+    onClickDelete: () -> Unit,
+) {
+    AnimatedVisibility(
+        visible = isVisible,
+        enter = fadeIn(),
+        exit = fadeOut(),
+    ) {
+        ExtendedFloatingActionButton(
+            modifier = Modifier.navigationBarsPadding(),
+            text = {
+                Text(text = stringResource(id = R.string.action_delete))
+            },
+            icon = {
+                Icon(Icons.Outlined.Delete, contentDescription = "")
+            },
+            onClick = onClickDelete,
+        )
+    }
+}

+ 53 - 0
app/src/main/java/eu/kanade/presentation/more/settings/database/components/ClearDatabaseItem.kt

@@ -0,0 +1,53 @@
+package eu.kanade.presentation.more.settings.database.components
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import eu.kanade.domain.source.model.Source
+import eu.kanade.presentation.browse.components.SourceIcon
+import eu.kanade.presentation.util.selectedBackground
+import eu.kanade.tachiyomi.R
+
+@Composable
+fun ClearDatabaseItem(
+    source: Source,
+    count: Long,
+    isSelected: Boolean,
+    onClickSelect: () -> Unit,
+) {
+    Row(
+        modifier = Modifier
+            .selectedBackground(isSelected)
+            .clickable(onClick = onClickSelect)
+            .padding(horizontal = 8.dp)
+            .height(56.dp),
+        verticalAlignment = Alignment.CenterVertically,
+    ) {
+        SourceIcon(source = source)
+        Column(
+            modifier = Modifier
+                .padding(start = 8.dp)
+                .weight(1f),
+        ) {
+            Text(
+                text = source.nameWithLanguage,
+                style = MaterialTheme.typography.bodyMedium,
+            )
+            Text(text = stringResource(id = R.string.clear_database_source_item_count, count))
+        }
+        Checkbox(
+            checked = isSelected,
+            onCheckedChange = { onClickSelect() },
+        )
+    }
+}

+ 42 - 0
app/src/main/java/eu/kanade/presentation/more/settings/database/components/ClearDatabaseToolbar.kt

@@ -0,0 +1,42 @@
+package eu.kanade.presentation.more.settings.database.components
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.FlipToBack
+import androidx.compose.material.icons.outlined.SelectAll
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.res.stringResource
+import eu.kanade.presentation.components.AppBar
+import eu.kanade.presentation.components.AppBarActions
+import eu.kanade.presentation.more.settings.database.ClearDatabaseState
+import eu.kanade.tachiyomi.R
+
+@Composable
+fun ClearDatabaseToolbar(
+    state: ClearDatabaseState,
+    navigateUp: () -> Unit,
+    onClickSelectAll: () -> Unit,
+    onClickInvertSelection: () -> Unit,
+) {
+    AppBar(
+        title = stringResource(id = R.string.pref_clear_database),
+        navigateUp = navigateUp,
+        actions = {
+            if (state.isEmpty.not()) {
+                AppBarActions(
+                    actions = listOf(
+                        AppBar.Action(
+                            title = stringResource(id = R.string.action_select_all),
+                            icon = Icons.Outlined.SelectAll,
+                            onClick = onClickSelectAll,
+                        ),
+                        AppBar.Action(
+                            title = stringResource(id = R.string.action_select_all),
+                            icon = Icons.Outlined.FlipToBack,
+                            onClick = onClickInvertSelection,
+                        ),
+                    ),
+                )
+            }
+        },
+    )
+}

+ 10 - 161
app/src/main/java/eu/kanade/tachiyomi/ui/setting/database/ClearDatabaseController.kt

@@ -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() },
+        )
     }
 }

+ 34 - 9
app/src/main/java/eu/kanade/tachiyomi/ui/setting/database/ClearDatabasePresenter.kt

@@ -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()
+    }
 }

+ 0 - 48
app/src/main/java/eu/kanade/tachiyomi/ui/setting/database/ClearDatabaseSourceItem.kt

@@ -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)
-        }
-    }
-}

+ 0 - 32
app/src/main/res/layout/clear_database_controller.xml

@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:tools="http://schemas.android.com/tools"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent">
-
-    <androidx.recyclerview.widget.RecyclerView
-        android:id="@+id/recycler"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:choiceMode="multipleChoice"
-        android:clipToPadding="false"
-        android:paddingBottom="@dimen/fab_list_padding"
-        tools:listitem="@layout/clear_database_source_item" />
-
-    <eu.kanade.tachiyomi.widget.MaterialFastScroll
-        android:id="@+id/fast_scroller"
-        android:layout_width="wrap_content"
-        android:layout_height="match_parent"
-        android:layout_gravity="end"
-        app:fastScrollerBubbleEnabled="false"
-        tools:visibility="visible" />
-
-    <eu.kanade.tachiyomi.widget.EmptyView
-        android:id="@+id/empty_view"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_gravity="center"
-        android:visibility="gone" />
-
-</FrameLayout>

+ 0 - 68
app/src/main/res/layout/clear_database_source_item.xml

@@ -1,68 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    xmlns:app="http://schemas.android.com/apk/res-auto"
-    xmlns:tools="http://schemas.android.com/tools"
-    android:layout_width="match_parent"
-    android:layout_height="56dp"
-    android:background="@drawable/list_item_selector_background"
-    android:paddingStart="8dp"
-    android:paddingEnd="16dp">
-
-
-    <ImageView
-        android:id="@+id/thumbnail"
-        android:layout_width="0dp"
-        android:layout_height="0dp"
-        android:paddingHorizontal="8dp"
-        app:layout_constraintDimensionRatio="1:1"
-        app:layout_constraintStart_toStartOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        tools:ignore="ContentDescription"
-        tools:src="@mipmap/ic_launcher" />
-
-    <TextView
-        android:id="@+id/title"
-        style="?attr/textAppearanceBodyMedium"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:paddingStart="0dp"
-        android:paddingEnd="8dp"
-        android:ellipsize="middle"
-        android:maxLines="1"
-        app:layout_constraintStart_toEndOf="@+id/thumbnail"
-        app:layout_constraintEnd_toStartOf="@id/checkbox"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toTopOf="@id/description"
-        app:layout_constraintVertical_chainStyle="packed"
-        tools:text="Source Name (LN)" />
-
-    <TextView
-        android:id="@+id/description"
-        style="?attr/textAppearanceBodySmall"
-        android:layout_width="0dp"
-        android:layout_height="wrap_content"
-        android:ellipsize="end"
-        android:maxLines="1"
-        app:layout_constraintStart_toEndOf="@+id/thumbnail"
-        app:layout_constraintEnd_toStartOf="@id/checkbox"
-        app:layout_constraintTop_toBottomOf="@id/title"
-        app:layout_constraintBottom_toBottomOf="parent"
-        tools:text="999 non-library manga in database" />
-
-    <CheckBox
-        android:id="@+id/checkbox"
-        android:layout_width="0dp"
-        android:layout_height="match_parent"
-        android:paddingStart="16dp"
-        android:paddingEnd="16dp"
-        android:clickable="false"
-        android:background="@android:color/transparent"
-        android:longClickable="false"
-        app:layout_constraintEnd_toEndOf="parent"
-        app:layout_constraintTop_toTopOf="parent"
-        app:layout_constraintBottom_toBottomOf="parent"
-        app:layout_constraintDimensionRatio="1:2"
-        />
-
-</androidx.constraintlayout.widget.ConstraintLayout>