refactor: 重构TXT目录规则界面为MVI架构

This commit is contained in:
HapeLee
2026-06-27 02:27:43 +08:00
parent da6c02e2ca
commit dd2190c091
4 changed files with 189 additions and 79 deletions
@@ -14,10 +14,8 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.SnackbarResult import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.rememberModalBottomSheetState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -44,29 +42,57 @@ import io.legado.app.ui.widget.components.card.ReorderableSelectionItem
import io.legado.app.ui.widget.components.filePicker.FilePickerSheet import io.legado.app.ui.widget.components.filePicker.FilePickerSheet
import io.legado.app.ui.widget.components.icon.AppIcons import io.legado.app.ui.widget.components.icon.AppIcons
import io.legado.app.ui.widget.components.importComponents.BatchImportDialog import io.legado.app.ui.widget.components.importComponents.BatchImportDialog
import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
import io.legado.app.ui.widget.components.importComponents.SourceInputDialog import io.legado.app.ui.widget.components.importComponents.SourceInputDialog
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem import io.legado.app.ui.widget.components.menuItem.RoundDropdownMenuItem
import io.legado.app.ui.widget.components.rules.RuleEditFields import io.legado.app.ui.widget.components.rules.RuleEditFields
import io.legado.app.ui.widget.components.rules.RuleEditSheet import io.legado.app.ui.widget.components.rules.RuleEditSheet
import io.legado.app.ui.widget.components.rules.RuleListScaffold import io.legado.app.ui.widget.components.rules.RuleListScaffold
import kotlinx.coroutines.flow.Flow
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
import sh.calvin.reorderable.rememberReorderableLazyListState import sh.calvin.reorderable.rememberReorderableLazyListState
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
fun TxtRuleScreen( fun TxtRuleRouteScreen(
viewModel: TxtTocRuleViewModel = koinViewModel(), viewModel: TxtTocRuleViewModel = koinViewModel(),
initialRule: String? = null, initialRule: String? = null,
onPickRule: ((String) -> Unit)? = null, onPickRule: ((String) -> Unit)? = null,
onBackClick: () -> Unit onBackClick: () -> Unit
) { ) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
val importState by viewModel.importState.collectAsStateWithLifecycle()
TxtRuleScreen(
state = uiState,
importState = importState,
events = viewModel.events,
onIntent = viewModel::onIntent,
onPasteRule = viewModel::pasteRule,
initialRule = initialRule,
onPickRule = onPickRule,
onBackClick = onBackClick,
)
}
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun TxtRuleScreen(
state: TxtTocRuleUiState,
importState: BaseImportUiState<TxtTocRule>,
events: Flow<BaseRuleEvent>,
onIntent: (TxtTocRuleIntent) -> Unit,
onPasteRule: () -> TxtTocRule?,
initialRule: String? = null,
onPickRule: ((String) -> Unit)? = null,
onBackClick: () -> Unit
) {
val context = LocalContext.current val context = LocalContext.current
val uiState by viewModel.uiState.collectAsState()
val rules = uiState.items val rules = state.items
val selectedIds = uiState.selectedIds val selectedIds = state.selectedIds
val isPickMode = onPickRule != null val isPickMode = onPickRule != null
val inSelectionMode = if (isPickMode) false else selectedIds.isNotEmpty() val inSelectionMode = if (isPickMode) false else selectedIds.isNotEmpty()
@@ -82,17 +108,15 @@ fun TxtRuleScreen(
var showExportSheet by remember { mutableStateOf(false) } var showExportSheet by remember { mutableStateOf(false) }
val reorderableState = rememberReorderableLazyListState(listState) { from, to -> val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
viewModel.moveItemInList(from.index, to.index) onIntent(TxtTocRuleIntent.MoveItem(from.index, to.index))
hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick) hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick)
} }
val clipboardManager = LocalClipboard.current val clipboardManager = LocalClipboard.current
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
val importState by viewModel.importState.collectAsStateWithLifecycle()
val sheetState = rememberModalBottomSheetState()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
viewModel.events.collect { event -> events.collect { event ->
when (event) { when (event) {
is BaseRuleEvent.ShowSnackbar -> { is BaseRuleEvent.ShowSnackbar -> {
val result = snackbarHostState.showSnackbar( val result = snackbarHostState.showSnackbar(
@@ -121,7 +145,7 @@ fun TxtRuleScreen(
uri?.let { uri?.let {
context.contentResolver.openInputStream(it)?.use { stream -> context.contentResolver.openInputStream(it)?.use { stream ->
val text = stream.reader().readText() val text = stream.reader().readText()
viewModel.importSource(text) onIntent(TxtTocRuleIntent.ImportSource(text))
} }
} }
} }
@@ -130,7 +154,7 @@ fun TxtRuleScreen(
val exportDoc = rememberLauncherForActivityResult( val exportDoc = rememberLauncherForActivityResult(
contract = ActivityResultContracts.CreateDocument("application/json"), contract = ActivityResultContracts.CreateDocument("application/json"),
onResult = { uri -> onResult = { uri ->
uri?.let { viewModel.exportToUri(it, rules, selectedIds) } uri?.let { onIntent(TxtTocRuleIntent.ExportSelection(it)) }
} }
) )
@@ -140,7 +164,7 @@ fun TxtRuleScreen(
onDismissRequest = { showUrlInput = false }, onDismissRequest = { showUrlInput = false },
onConfirm = { onConfirm = {
showUrlInput = false showUrlInput = false
viewModel.importSource(it) onIntent(TxtTocRuleIntent.ImportSource(it))
} }
) )
@@ -154,7 +178,7 @@ fun TxtRuleScreen(
}, },
onUpload = { onUpload = {
showExportSheet = false showExportSheet = false
viewModel.uploadSelectedRules(selectedIds, rules) onIntent(TxtTocRuleIntent.UploadSelection)
}, },
allowExtensions = arrayOf("json") allowExtensions = arrayOf("json")
) )
@@ -178,11 +202,11 @@ fun TxtRuleScreen(
BatchImportDialog( BatchImportDialog(
title = stringResource(R.string.import_txt_toc_rule), title = stringResource(R.string.import_txt_toc_rule),
importState = importState, importState = importState,
onDismissRequest = { viewModel.cancelImport() }, onDismissRequest = { onIntent(TxtTocRuleIntent.CancelImport) },
onToggleItem = { viewModel.toggleImportSelection(it) }, onToggleItem = { onIntent(TxtTocRuleIntent.ToggleImportSelection(it)) },
onToggleAll = { viewModel.toggleImportAll(it) }, onToggleAll = { onIntent(TxtTocRuleIntent.ToggleImportAll(it)) },
onUpdateItem = { index, rule -> viewModel.updateImportItem(index, rule) }, onUpdateItem = { index, rule -> onIntent(TxtTocRuleIntent.UpdateImportItem(index, rule)) },
onConfirm = { viewModel.saveImportedRules() }, onConfirm = { onIntent(TxtTocRuleIntent.SaveImportedRules) },
itemTitle = { rule -> rule.name }, itemTitle = { rule -> rule.name },
itemSubtitle = { rule -> itemSubtitle = { rule ->
rule.rule.takeIf { it.isNotBlank() } rule.rule.takeIf { it.isNotBlank() }
@@ -191,7 +215,7 @@ fun TxtRuleScreen(
LaunchedEffect(reorderableState.isAnyItemDragging) { LaunchedEffect(reorderableState.isAnyItemDragging) {
if (!reorderableState.isAnyItemDragging) { if (!reorderableState.isAnyItemDragging) {
viewModel.saveSortOrder() onIntent(TxtTocRuleIntent.SaveSortOrder)
} }
} }
@@ -202,7 +226,7 @@ fun TxtRuleScreen(
text = stringResource(R.string.sure_del), text = stringResource(R.string.sure_del),
confirmText = stringResource(R.string.ok), confirmText = stringResource(R.string.ok),
onConfirm = { rule -> onConfirm = { rule ->
viewModel.delete(rule) onIntent(TxtTocRuleIntent.DeleteRule(rule))
showDeleteRuleDialog = null showDeleteRuleDialog = null
}, },
dismissText = stringResource(R.string.cancel), dismissText = stringResource(R.string.cancel),
@@ -220,17 +244,12 @@ fun TxtRuleScreen(
editingRule = null editingRule = null
}, },
onSave = { updatedRule -> onSave = { updatedRule ->
//TODO:我很想把他改为自增主键,但为了兼容性日后再? onIntent(TxtTocRuleIntent.SaveRule(updatedRule, isNew = editingRule == null))
if (editingRule == null) {
viewModel.insert(updatedRule)
} else {
viewModel.update(updatedRule)
}
showEditSheet = false showEditSheet = false
editingRule = null editingRule = null
}, },
onCopy = { viewModel.copyRule(it) }, onCopy = { onIntent(TxtTocRuleIntent.CopyRule(it)) },
onPaste = { viewModel.pasteRule() }, onPaste = onPasteRule,
toFields = { r -> toFields = { r ->
RuleEditFields( RuleEditFields(
name = r?.name ?: "", name = r?.name ?: "",
@@ -257,27 +276,22 @@ fun TxtRuleScreen(
} else { } else {
stringResource(R.string.txt_toc_rule) stringResource(R.string.txt_toc_rule)
}, },
state = uiState, state = state,
onBackClick = { onBackClick() }, onBackClick = { onBackClick() },
onSearchToggle = { active -> onSearchToggle = { active ->
viewModel.setSearchMode(active) onIntent(TxtTocRuleIntent.SetSearchMode(active))
}, },
onSearchQueryChange = { viewModel.setSearchKey(it) }, onSearchQueryChange = { onIntent(TxtTocRuleIntent.UpdateSearchQuery(it)) },
searchPlaceholder = stringResource(R.string.replace_purify_search), searchPlaceholder = stringResource(R.string.replace_purify_search),
onClearSelection = { viewModel.setSelection(emptySet()) }, onClearSelection = { onIntent(TxtTocRuleIntent.ClearSelection) },
onSelectAll = { viewModel.setSelection(rules.map { it.id }.toSet()) }, onSelectAll = { onIntent(TxtTocRuleIntent.SelectAll) },
onSelectInvert = { onSelectInvert = { onIntent(TxtTocRuleIntent.InvertSelection) },
val allIds = rules.map { it.id }.toSet()
viewModel.setSelection(allIds - selectedIds)
},
selectionSecondaryActions = listOf( selectionSecondaryActions = listOf(
ActionItem(text = stringResource(R.string.enable), onClick = { ActionItem(text = stringResource(R.string.enable), onClick = {
viewModel.enableSelectionByIds(selectedIds) onIntent(TxtTocRuleIntent.EnableSelection)
viewModel.setSelection(emptySet())
}), }),
ActionItem(text = stringResource(R.string.disable_selection), onClick = { ActionItem(text = stringResource(R.string.disable_selection), onClick = {
viewModel.disableSelectionByIds(selectedIds) onIntent(TxtTocRuleIntent.DisableSelection)
viewModel.setSelection(emptySet())
}), }),
ActionItem( ActionItem(
text = stringResource(R.string.export), text = stringResource(R.string.export),
@@ -285,8 +299,8 @@ fun TxtRuleScreen(
), ),
onDeleteSelected = { ids -> onDeleteSelected = { ids ->
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
viewModel.delSelectionByIds(ids as Set<Long>) onIntent(TxtTocRuleIntent.SetSelection(ids as Set<Long>))
viewModel.setSelection(emptySet()) onIntent(TxtTocRuleIntent.DeleteSelection)
}, },
onAddClick = { onAddClick = {
editingRule = null editingRule = null
@@ -334,10 +348,12 @@ fun TxtRuleScreen(
onPickRule.invoke(item.rule.rule) onPickRule.invoke(item.rule.rule)
onBackClick() onBackClick()
} else { } else {
viewModel.toggleSelection(item.id) onIntent(TxtTocRuleIntent.ToggleSelection(item.id))
} }
}, },
onEnabledChange = { enabled -> viewModel.update(item.rule.copy(enable = enabled)) }, onEnabledChange = { enabled ->
onIntent(TxtTocRuleIntent.SetRuleEnabled(item.rule, enabled))
},
onClickEdit = { editingRule = item.rule; showEditSheet = true }, onClickEdit = { editingRule = item.rule; showEditSheet = true },
trailingAction = { trailingAction = {
SmallPlainButton( SmallPlainButton(
@@ -353,7 +369,7 @@ fun TxtRuleScreen(
listState = listState, listState = listState,
items = rules, items = rules,
selectedIds = selectedIds, selectedIds = selectedIds,
onSelectionChange = { viewModel.setSelection(it) }, onSelectionChange = { onIntent(TxtTocRuleIntent.SetSelection(it)) },
idProvider = { it.id }, idProvider = { it.id },
modifier = Modifier modifier = Modifier
.fillMaxHeight() .fillMaxHeight()
@@ -364,4 +380,3 @@ fun TxtRuleScreen(
} }
} }
} }
@@ -13,7 +13,7 @@ class TxtTocRuleActivity : BaseComposeActivity() {
val initialRule = intent.getStringExtra("tocRegex") val initialRule = intent.getStringExtra("tocRegex")
AppTheme { AppTheme {
TxtRuleScreen( TxtRuleRouteScreen(
initialRule = initialRule, initialRule = initialRule,
onPickRule = if (initialRule != null) { rule -> onPickRule = if (initialRule != null) { rule ->
val data = Intent().apply { val data = Intent().apply {
@@ -0,0 +1,62 @@
package io.legado.app.ui.book.toc.rule
import android.net.Uri
import androidx.compose.runtime.Immutable
import io.legado.app.data.entities.TxtTocRule
import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
import io.legado.app.ui.widget.components.list.InteractionState
import io.legado.app.ui.widget.components.list.ListUiState
import io.legado.app.ui.widget.components.list.SelectableItem
@Immutable
data class TxtTocRuleItemUi(
override val id: Long,
val name: String,
val isEnabled: Boolean,
val rule: TxtTocRule,
val example: String = ""
) : SelectableItem<Long>
data class TxtTocRuleUiState(
override val items: List<TxtTocRuleItemUi> = emptyList(),
override val selectedIds: Set<Long> = emptySet(),
override val searchKey: String = "",
val interaction: InteractionState = InteractionState()
) : ListUiState<TxtTocRuleItemUi> {
override val isSearch: Boolean get() = interaction.isSearchMode
override val isLoading: Boolean get() = interaction.isUploading
}
sealed interface TxtTocRuleIntent {
data class SetSearchMode(val active: Boolean) : TxtTocRuleIntent
data class UpdateSearchQuery(val query: String) : TxtTocRuleIntent
data object ClearSelection : TxtTocRuleIntent
data object SelectAll : TxtTocRuleIntent
data object InvertSelection : TxtTocRuleIntent
data class SetSelection(val ids: Set<Long>) : TxtTocRuleIntent
data class ToggleSelection(val id: Long) : TxtTocRuleIntent
data object EnableSelection : TxtTocRuleIntent
data object DisableSelection : TxtTocRuleIntent
data object DeleteSelection : TxtTocRuleIntent
data object UploadSelection : TxtTocRuleIntent
data class ExportSelection(val uri: Uri) : TxtTocRuleIntent
data class MoveItem(val from: Int, val to: Int) : TxtTocRuleIntent
data object SaveSortOrder : TxtTocRuleIntent
data class SaveRule(val rule: TxtTocRule, val isNew: Boolean) : TxtTocRuleIntent
data class DeleteRule(val rule: TxtTocRule) : TxtTocRuleIntent
data class SetRuleEnabled(val rule: TxtTocRule, val enabled: Boolean) : TxtTocRuleIntent
data class CopyRule(val rule: TxtTocRule) : TxtTocRuleIntent
data class ImportSource(val text: String) : TxtTocRuleIntent
data object CancelImport : TxtTocRuleIntent
data class ToggleImportSelection(val index: Int) : TxtTocRuleIntent
data class ToggleImportAll(val isSelected: Boolean) : TxtTocRuleIntent
data class UpdateImportItem(val index: Int, val rule: TxtTocRule) : TxtTocRuleIntent
data object SaveImportedRules : TxtTocRuleIntent
}
sealed interface TxtTocRuleEffect
data class TxtTocRuleRenderState(
val uiState: TxtTocRuleUiState,
val importState: BaseImportUiState<TxtTocRule>,
)
@@ -1,7 +1,6 @@
package io.legado.app.ui.book.toc.rule package io.legado.app.ui.book.toc.rule
import android.app.Application import android.app.Application
import androidx.compose.runtime.Immutable
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.BaseRuleViewModel import io.legado.app.base.BaseRuleViewModel
@@ -10,8 +9,6 @@ import io.legado.app.data.repository.TxtTocRuleRepository
import io.legado.app.data.repository.UploadRepository import io.legado.app.data.repository.UploadRepository
import io.legado.app.ui.widget.components.importComponents.BaseImportUiState import io.legado.app.ui.widget.components.importComponents.BaseImportUiState
import io.legado.app.ui.widget.components.list.InteractionState import io.legado.app.ui.widget.components.list.InteractionState
import io.legado.app.ui.widget.components.list.ListUiState
import io.legado.app.ui.widget.components.list.SelectableItem
import io.legado.app.utils.GSON import io.legado.app.utils.GSON
import io.legado.app.utils.fromJsonArray import io.legado.app.utils.fromJsonArray
import io.legado.app.utils.fromJsonObject import io.legado.app.utils.fromJsonObject
@@ -21,28 +18,9 @@ import io.legado.app.utils.isJsonObject
import io.legado.app.utils.sendToClip import io.legado.app.utils.sendToClip
import io.legado.app.utils.toastOnUi import io.legado.app.utils.toastOnUi
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Immutable
data class TxtTocRuleItemUi(
override val id: Long,
val name: String,
val isEnabled: Boolean,
val rule: TxtTocRule,
val example: String = ""
) : SelectableItem<Long>
data class TxtTocRuleUiState(
override val items: List<TxtTocRuleItemUi> = emptyList(),
override val selectedIds: Set<Long> = emptySet(),
override val searchKey: String = "",
val interaction: InteractionState = InteractionState()
) : ListUiState<TxtTocRuleItemUi> {
override val isSearch: Boolean get() = interaction.isSearchMode
override val isLoading: Boolean get() = interaction.isUploading
}
class TxtTocRuleViewModel( class TxtTocRuleViewModel(
application: Application, application: Application,
uploadRepository: UploadRepository uploadRepository: UploadRepository
@@ -55,9 +33,54 @@ class TxtTocRuleViewModel(
override val rawDataFlow: Flow<List<TxtTocRule>> = repository.flowAll() override val rawDataFlow: Flow<List<TxtTocRule>> = repository.flowAll()
fun onIntent(intent: TxtTocRuleIntent) {
when (intent) {
is TxtTocRuleIntent.SetSearchMode -> setSearchMode(intent.active)
is TxtTocRuleIntent.UpdateSearchQuery -> setSearchKey(intent.query)
TxtTocRuleIntent.ClearSelection -> setSelection(emptySet())
TxtTocRuleIntent.SelectAll -> selectAll()
TxtTocRuleIntent.InvertSelection -> invertSelection()
is TxtTocRuleIntent.SetSelection -> setSelection(intent.ids)
is TxtTocRuleIntent.ToggleSelection -> toggleSelection(intent.id)
TxtTocRuleIntent.EnableSelection -> {
enableSelectionByIds(uiState.value.selectedIds)
setSelection(emptySet())
}
TxtTocRuleIntent.DisableSelection -> {
disableSelectionByIds(uiState.value.selectedIds)
setSelection(emptySet())
}
TxtTocRuleIntent.DeleteSelection -> {
delSelectionByIds(uiState.value.selectedIds)
setSelection(emptySet())
}
TxtTocRuleIntent.UploadSelection -> {
val state = uiState.value
uploadSelectedRules(state.selectedIds, state.items)
}
is TxtTocRuleIntent.ExportSelection -> {
val state = uiState.value
exportToUri(intent.uri, state.items, state.selectedIds)
}
is TxtTocRuleIntent.MoveItem -> moveItemInList(intent.from, intent.to)
TxtTocRuleIntent.SaveSortOrder -> saveSortOrder()
is TxtTocRuleIntent.SaveRule -> save(intent.rule)
is TxtTocRuleIntent.DeleteRule -> delete(intent.rule)
is TxtTocRuleIntent.SetRuleEnabled -> update(intent.rule.copy(enable = intent.enabled))
is TxtTocRuleIntent.CopyRule -> copyRule(intent.rule)
is TxtTocRuleIntent.ImportSource -> importSource(intent.text)
TxtTocRuleIntent.CancelImport -> cancelImport()
is TxtTocRuleIntent.ToggleImportSelection -> toggleImportSelection(intent.index)
is TxtTocRuleIntent.ToggleImportAll -> toggleImportAll(intent.isSelected)
is TxtTocRuleIntent.UpdateImportItem -> updateImportItem(intent.index, intent.rule)
TxtTocRuleIntent.SaveImportedRules -> saveImportedRules()
}
}
override fun TxtTocRule.toUiItem() = override fun TxtTocRule.toUiItem() =
TxtTocRuleItemUi(id, name, enable, this, example = example ?: "") TxtTocRuleItemUi(id, name, enable, this, example = example ?: "")
@Suppress("DEPRECATION")
override fun filterData(data: List<TxtTocRule>, key: String): List<TxtTocRule> { override fun filterData(data: List<TxtTocRule>, key: String): List<TxtTocRule> {
val filtered = if (key.isEmpty()) data val filtered = if (key.isEmpty()) data
else data.filter { it.name.contains(key, ignoreCase = true) } else data.filter { it.name.contains(key, ignoreCase = true) }
@@ -83,7 +106,7 @@ class TxtTocRuleViewModel(
) )
} }
fun saveSortOrder() { private fun saveSortOrder() {
val currentLocal = _localItems.value ?: return val currentLocal = _localItems.value ?: return
viewModelScope.launch { viewModelScope.launch {
repository.saveOrder(currentLocal.map { it.rule }) repository.saveOrder(currentLocal.map { it.rule })
@@ -91,7 +114,7 @@ class TxtTocRuleViewModel(
} }
} }
fun save(rule: TxtTocRule) { private fun save(rule: TxtTocRule) {
viewModelScope.launch { viewModelScope.launch {
if (rule.id == 0L) { if (rule.id == 0L) {
repository.insert(rule) repository.insert(rule)
@@ -101,9 +124,10 @@ class TxtTocRuleViewModel(
} }
} }
fun update(vararg rules: TxtTocRule) = viewModelScope.launch { repository.update(*rules) } private fun update(vararg rules: TxtTocRule) = viewModelScope.launch { repository.update(*rules) }
fun insert(vararg rules: TxtTocRule) = viewModelScope.launch { repository.insert(*rules) } private fun insert(vararg rules: TxtTocRule) = viewModelScope.launch { repository.insert(*rules) }
fun delete(vararg rules: TxtTocRule) = viewModelScope.launch { repository.delete(*rules) } private fun delete(vararg rules: TxtTocRule) = viewModelScope.launch { repository.delete(*rules) }
fun enableSelectionByIds(ids: Set<Long>) = fun enableSelectionByIds(ids: Set<Long>) =
viewModelScope.launch { repository.enableByIds(ids, true) } viewModelScope.launch { repository.enableByIds(ids, true) }
@@ -112,6 +136,15 @@ class TxtTocRuleViewModel(
fun delSelectionByIds(ids: Set<Long>) = viewModelScope.launch { repository.deleteByIds(ids) } fun delSelectionByIds(ids: Set<Long>) = viewModelScope.launch { repository.deleteByIds(ids) }
private fun selectAll() {
setSelection(uiState.value.items.map { it.id }.toSet())
}
private fun invertSelection() {
val state = uiState.value
setSelection(state.items.map { it.id }.toSet() - state.selectedIds)
}
override suspend fun generateJson(entities: List<TxtTocRule>): String = GSON.toJson(entities) override suspend fun generateJson(entities: List<TxtTocRule>): String = GSON.toJson(entities)
override fun parseImportRules(text: String): List<TxtTocRule> { override fun parseImportRules(text: String): List<TxtTocRule> {
@@ -141,7 +174,7 @@ class TxtTocRuleViewModel(
} }
} }
fun copyRule(rule: TxtTocRule) { private fun copyRule(rule: TxtTocRule) {
context.sendToClip(GSON.toJson(rule)) context.sendToClip(GSON.toJson(rule))
} }