mirror of
https://github.com/tachiyomiorg/tachiyomi.git
synced 2024-06-28 08:56:04 +02:00
264 lines
11 KiB
Kotlin
264 lines
11 KiB
Kotlin
package eu.kanade.presentation.more.settings.screen.advanced
|
|
|
|
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.foundation.lazy.items
|
|
import androidx.compose.material.icons.Icons
|
|
import androidx.compose.material.icons.outlined.FlipToBack
|
|
import androidx.compose.material.icons.outlined.SelectAll
|
|
import androidx.compose.material3.AlertDialog
|
|
import androidx.compose.material3.Checkbox
|
|
import androidx.compose.material3.MaterialTheme
|
|
import androidx.compose.material3.Text
|
|
import androidx.compose.material3.TextButton
|
|
import androidx.compose.runtime.Composable
|
|
import androidx.compose.runtime.Immutable
|
|
import androidx.compose.runtime.collectAsState
|
|
import androidx.compose.runtime.getValue
|
|
import androidx.compose.runtime.rememberCoroutineScope
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.platform.LocalContext
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.compose.ui.util.fastMap
|
|
import cafe.adriel.voyager.core.model.StateScreenModel
|
|
import cafe.adriel.voyager.core.model.rememberScreenModel
|
|
import cafe.adriel.voyager.core.model.screenModelScope
|
|
import cafe.adriel.voyager.navigator.LocalNavigator
|
|
import cafe.adriel.voyager.navigator.currentOrThrow
|
|
import eu.kanade.presentation.browse.components.SourceIcon
|
|
import eu.kanade.presentation.components.AppBar
|
|
import eu.kanade.presentation.components.AppBarActions
|
|
import eu.kanade.presentation.util.Screen
|
|
import eu.kanade.tachiyomi.util.system.toast
|
|
import kotlinx.collections.immutable.persistentListOf
|
|
import kotlinx.coroutines.flow.collectLatest
|
|
import kotlinx.coroutines.flow.update
|
|
import tachiyomi.core.util.lang.launchIO
|
|
import tachiyomi.core.util.lang.launchUI
|
|
import tachiyomi.core.util.lang.withNonCancellableContext
|
|
import tachiyomi.data.Database
|
|
import tachiyomi.domain.source.interactor.GetSourcesWithNonLibraryManga
|
|
import tachiyomi.domain.source.model.Source
|
|
import tachiyomi.domain.source.model.SourceWithCount
|
|
import tachiyomi.i18n.MR
|
|
import tachiyomi.presentation.core.components.LazyColumnWithAction
|
|
import tachiyomi.presentation.core.components.material.Scaffold
|
|
import tachiyomi.presentation.core.i18n.stringResource
|
|
import tachiyomi.presentation.core.screens.EmptyScreen
|
|
import tachiyomi.presentation.core.screens.LoadingScreen
|
|
import tachiyomi.presentation.core.util.selectedBackground
|
|
import uy.kohesive.injekt.Injekt
|
|
import uy.kohesive.injekt.api.get
|
|
|
|
class ClearDatabaseScreen : Screen() {
|
|
|
|
@Composable
|
|
override fun Content() {
|
|
val context = LocalContext.current
|
|
val navigator = LocalNavigator.currentOrThrow
|
|
val model = rememberScreenModel { ClearDatabaseScreenModel() }
|
|
val state by model.state.collectAsState()
|
|
val scope = rememberCoroutineScope()
|
|
|
|
when (val s = state) {
|
|
is ClearDatabaseScreenModel.State.Loading -> LoadingScreen()
|
|
is ClearDatabaseScreenModel.State.Ready -> {
|
|
if (s.showConfirmation) {
|
|
AlertDialog(
|
|
onDismissRequest = model::hideConfirmation,
|
|
confirmButton = {
|
|
TextButton(
|
|
onClick = {
|
|
scope.launchUI {
|
|
model.removeMangaBySourceId()
|
|
model.clearSelection()
|
|
model.hideConfirmation()
|
|
context.toast(MR.strings.clear_database_completed)
|
|
}
|
|
},
|
|
) {
|
|
Text(text = stringResource(MR.strings.action_ok))
|
|
}
|
|
},
|
|
dismissButton = {
|
|
TextButton(onClick = model::hideConfirmation) {
|
|
Text(text = stringResource(MR.strings.action_cancel))
|
|
}
|
|
},
|
|
text = {
|
|
Text(text = stringResource(MR.strings.clear_database_confirmation))
|
|
},
|
|
)
|
|
}
|
|
|
|
Scaffold(
|
|
topBar = { scrollBehavior ->
|
|
AppBar(
|
|
title = stringResource(MR.strings.pref_clear_database),
|
|
navigateUp = navigator::pop,
|
|
actions = {
|
|
if (s.items.isNotEmpty()) {
|
|
AppBarActions(
|
|
actions = persistentListOf(
|
|
AppBar.Action(
|
|
title = stringResource(MR.strings.action_select_all),
|
|
icon = Icons.Outlined.SelectAll,
|
|
onClick = model::selectAll,
|
|
),
|
|
AppBar.Action(
|
|
title = stringResource(MR.strings.action_select_inverse),
|
|
icon = Icons.Outlined.FlipToBack,
|
|
onClick = model::invertSelection,
|
|
),
|
|
),
|
|
)
|
|
}
|
|
},
|
|
scrollBehavior = scrollBehavior,
|
|
)
|
|
},
|
|
) { contentPadding ->
|
|
if (s.items.isEmpty()) {
|
|
EmptyScreen(
|
|
message = stringResource(MR.strings.database_clean),
|
|
modifier = Modifier.padding(contentPadding),
|
|
)
|
|
} else {
|
|
LazyColumnWithAction(
|
|
contentPadding = contentPadding,
|
|
actionLabel = stringResource(MR.strings.action_delete),
|
|
actionEnabled = s.selection.isNotEmpty(),
|
|
onClickAction = model::showConfirmation,
|
|
) {
|
|
items(s.items) { sourceWithCount ->
|
|
ClearDatabaseItem(
|
|
source = sourceWithCount.source,
|
|
count = sourceWithCount.count,
|
|
isSelected = s.selection.contains(sourceWithCount.id),
|
|
onClickSelect = { model.toggleSelection(sourceWithCount.source) },
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
private 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.visualName,
|
|
style = MaterialTheme.typography.bodyMedium,
|
|
)
|
|
Text(text = stringResource(MR.strings.clear_database_source_item_count, count))
|
|
}
|
|
Checkbox(
|
|
checked = isSelected,
|
|
onCheckedChange = { onClickSelect() },
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
private class ClearDatabaseScreenModel : StateScreenModel<ClearDatabaseScreenModel.State>(State.Loading) {
|
|
private val getSourcesWithNonLibraryManga: GetSourcesWithNonLibraryManga = Injekt.get()
|
|
private val database: Database = Injekt.get()
|
|
|
|
init {
|
|
screenModelScope.launchIO {
|
|
getSourcesWithNonLibraryManga.subscribe()
|
|
.collectLatest { list ->
|
|
mutableState.update { old ->
|
|
val items = list.sortedBy { it.name }
|
|
when (old) {
|
|
State.Loading -> State.Ready(items)
|
|
is State.Ready -> old.copy(items = items)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
suspend fun removeMangaBySourceId() = withNonCancellableContext {
|
|
val state = state.value as? State.Ready ?: return@withNonCancellableContext
|
|
database.mangasQueries.deleteMangasNotInLibraryBySourceIds(state.selection)
|
|
database.historyQueries.removeResettedHistory()
|
|
}
|
|
|
|
fun toggleSelection(source: Source) = mutableState.update { state ->
|
|
if (state !is State.Ready) return@update state
|
|
val mutableList = state.selection.toMutableList()
|
|
if (mutableList.contains(source.id)) {
|
|
mutableList.remove(source.id)
|
|
} else {
|
|
mutableList.add(source.id)
|
|
}
|
|
state.copy(selection = mutableList)
|
|
}
|
|
|
|
fun clearSelection() = mutableState.update { state ->
|
|
if (state !is State.Ready) return@update state
|
|
state.copy(selection = emptyList())
|
|
}
|
|
|
|
fun selectAll() = mutableState.update { state ->
|
|
if (state !is State.Ready) return@update state
|
|
state.copy(selection = state.items.fastMap { it.id })
|
|
}
|
|
|
|
fun invertSelection() = mutableState.update { state ->
|
|
if (state !is State.Ready) return@update state
|
|
state.copy(
|
|
selection = state.items
|
|
.fastMap { it.id }
|
|
.filterNot { it in state.selection },
|
|
)
|
|
}
|
|
|
|
fun showConfirmation() = mutableState.update { state ->
|
|
if (state !is State.Ready) return@update state
|
|
state.copy(showConfirmation = true)
|
|
}
|
|
|
|
fun hideConfirmation() = mutableState.update { state ->
|
|
if (state !is State.Ready) return@update state
|
|
state.copy(showConfirmation = false)
|
|
}
|
|
|
|
sealed interface State {
|
|
@Immutable
|
|
data object Loading : State
|
|
|
|
@Immutable
|
|
data class Ready(
|
|
val items: List<SourceWithCount>,
|
|
val selection: List<Long> = emptyList(),
|
|
val showConfirmation: Boolean = false,
|
|
) : State
|
|
}
|
|
}
|