fix:修复词典规则页状态同步问题
This commit is contained in:
4 files changed
+199
-70
No files matched your search
@@ -10,7 +10,7 @@ class DictRuleActivity : BaseComposeActivity() {
|
|||||||
@Composable
|
@Composable
|
||||||
override fun Content() {
|
override fun Content() {
|
||||||
AppTheme {
|
AppTheme {
|
||||||
DictRuleScreen(onBackClick = { finish() })
|
DictRuleRouteScreen(onBackClick = { finish() })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
package io.legado.app.ui.dict.rule
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import androidx.compose.runtime.Stable
|
||||||
|
import io.legado.app.data.entities.DictRule
|
||||||
|
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
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.ImmutableSet
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.persistentSetOf
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class DictRuleItemUi(
|
||||||
|
override val id: String,
|
||||||
|
val urlRule: String,
|
||||||
|
val showRule: String,
|
||||||
|
val isEnabled: Boolean,
|
||||||
|
val rule: DictRule
|
||||||
|
) : SelectableItem<String>
|
||||||
|
|
||||||
|
@Stable
|
||||||
|
data class DictRuleUiState(
|
||||||
|
override val items: ImmutableList<DictRuleItemUi> = persistentListOf(),
|
||||||
|
override val selectedIds: ImmutableSet<String> = persistentSetOf(),
|
||||||
|
override val searchKey: String = "",
|
||||||
|
val interaction: InteractionState = InteractionState()
|
||||||
|
) : ListUiState<DictRuleItemUi> {
|
||||||
|
override val isSearch: Boolean get() = interaction.isSearchMode
|
||||||
|
override val isLoading: Boolean get() = interaction.isUploading
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface DictRuleIntent {
|
||||||
|
data class SetSearchMode(val active: Boolean) : DictRuleIntent
|
||||||
|
data class UpdateSearchQuery(val query: String) : DictRuleIntent
|
||||||
|
data object ClearSelection : DictRuleIntent
|
||||||
|
data object SelectAll : DictRuleIntent
|
||||||
|
data object InvertSelection : DictRuleIntent
|
||||||
|
data class SetSelection(val ids: Set<String>) : DictRuleIntent
|
||||||
|
data class ToggleSelection(val id: String) : DictRuleIntent
|
||||||
|
data object EnableSelection : DictRuleIntent
|
||||||
|
data object DisableSelection : DictRuleIntent
|
||||||
|
data object DeleteSelection : DictRuleIntent
|
||||||
|
data object UploadSelection : DictRuleIntent
|
||||||
|
data class ExportSelection(val uri: Uri) : DictRuleIntent
|
||||||
|
data class MoveItem(val from: Int, val to: Int) : DictRuleIntent
|
||||||
|
data object SaveSortOrder : DictRuleIntent
|
||||||
|
data class SaveRule(val rule: DictRule, val isNew: Boolean) : DictRuleIntent
|
||||||
|
data class DeleteRule(val rule: DictRule) : DictRuleIntent
|
||||||
|
data class SetRuleEnabled(val rule: DictRule, val enabled: Boolean) : DictRuleIntent
|
||||||
|
data class CopyRule(val rule: DictRule) : DictRuleIntent
|
||||||
|
data class ImportSource(val text: String) : DictRuleIntent
|
||||||
|
data object CancelImport : DictRuleIntent
|
||||||
|
data class ToggleImportSelection(val index: Int) : DictRuleIntent
|
||||||
|
data class ToggleImportAll(val isSelected: Boolean) : DictRuleIntent
|
||||||
|
data class UpdateImportItem(val index: Int, val rule: DictRule) : DictRuleIntent
|
||||||
|
data object SaveImportedRules : DictRuleIntent
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed interface DictRuleEffect
|
||||||
|
|
||||||
|
data class DictRuleRenderState(
|
||||||
|
val uiState: DictRuleUiState,
|
||||||
|
val importState: BaseImportUiState<DictRule>,
|
||||||
|
)
|
||||||
@@ -16,7 +16,6 @@ import androidx.compose.material3.SnackbarHostState
|
|||||||
import androidx.compose.material3.SnackbarResult
|
import androidx.compose.material3.SnackbarResult
|
||||||
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
|
||||||
@@ -43,27 +42,51 @@ 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 DictRuleScreen(
|
fun DictRuleRouteScreen(
|
||||||
viewModel: DictRuleViewModel = koinViewModel(),
|
viewModel: DictRuleViewModel = koinViewModel(),
|
||||||
onBackClick: () -> Unit
|
onBackClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||||
|
val importState by viewModel.importState.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
|
DictRuleScreen(
|
||||||
|
state = uiState,
|
||||||
|
importState = importState,
|
||||||
|
events = viewModel.events,
|
||||||
|
onIntent = viewModel::onIntent,
|
||||||
|
onPasteRule = viewModel::pasteRule,
|
||||||
|
onBackClick = onBackClick,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterial3ExpressiveApi::class)
|
||||||
|
@Composable
|
||||||
|
fun DictRuleScreen(
|
||||||
|
state: DictRuleUiState,
|
||||||
|
importState: BaseImportUiState<DictRule>,
|
||||||
|
events: Flow<BaseRuleEvent>,
|
||||||
|
onIntent: (DictRuleIntent) -> Unit,
|
||||||
|
onPasteRule: () -> DictRule?,
|
||||||
|
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 inSelectionMode = selectedIds.isNotEmpty()
|
val inSelectionMode = selectedIds.isNotEmpty()
|
||||||
|
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
@@ -79,16 +102,15 @@ fun DictRuleScreen(
|
|||||||
|
|
||||||
|
|
||||||
val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
|
val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
|
||||||
viewModel.moveItemInList(from.index, to.index)
|
onIntent(DictRuleIntent.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()
|
|
||||||
|
|
||||||
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(
|
||||||
@@ -117,7 +139,7 @@ fun DictRuleScreen(
|
|||||||
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(DictRuleIntent.ImportSource(text))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -126,7 +148,7 @@ fun DictRuleScreen(
|
|||||||
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(DictRuleIntent.ExportSelection(it)) }
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -136,7 +158,7 @@ fun DictRuleScreen(
|
|||||||
onDismissRequest = { showUrlInput = false },
|
onDismissRequest = { showUrlInput = false },
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
showUrlInput = false
|
showUrlInput = false
|
||||||
viewModel.importSource(it)
|
onIntent(DictRuleIntent.ImportSource(it))
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -150,7 +172,7 @@ fun DictRuleScreen(
|
|||||||
},
|
},
|
||||||
onUpload = {
|
onUpload = {
|
||||||
showExportSheet = false
|
showExportSheet = false
|
||||||
viewModel.uploadSelectedRules(selectedIds, rules)
|
onIntent(DictRuleIntent.UploadSelection)
|
||||||
},
|
},
|
||||||
allowExtensions = arrayOf("json")
|
allowExtensions = arrayOf("json")
|
||||||
)
|
)
|
||||||
@@ -174,11 +196,11 @@ fun DictRuleScreen(
|
|||||||
BatchImportDialog(
|
BatchImportDialog(
|
||||||
title = "导入词典规则",
|
title = "导入词典规则",
|
||||||
importState = importState,
|
importState = importState,
|
||||||
onDismissRequest = { viewModel.cancelImport() },
|
onDismissRequest = { onIntent(DictRuleIntent.CancelImport) },
|
||||||
onToggleItem = { viewModel.toggleImportSelection(it) },
|
onToggleItem = { onIntent(DictRuleIntent.ToggleImportSelection(it)) },
|
||||||
onToggleAll = { viewModel.toggleImportAll(it) },
|
onToggleAll = { onIntent(DictRuleIntent.ToggleImportAll(it)) },
|
||||||
onUpdateItem = { index, rule -> viewModel.updateImportItem(index, rule) },
|
onUpdateItem = { index, rule -> onIntent(DictRuleIntent.UpdateImportItem(index, rule)) },
|
||||||
onConfirm = { viewModel.saveImportedRules() },
|
onConfirm = { onIntent(DictRuleIntent.SaveImportedRules) },
|
||||||
itemTitle = { rule -> rule.name },
|
itemTitle = { rule -> rule.name },
|
||||||
itemSubtitle = { rule ->
|
itemSubtitle = { rule ->
|
||||||
rule.urlRule.takeIf { it.isNotBlank() }
|
rule.urlRule.takeIf { it.isNotBlank() }
|
||||||
@@ -187,7 +209,7 @@ fun DictRuleScreen(
|
|||||||
|
|
||||||
LaunchedEffect(reorderableState.isAnyItemDragging) {
|
LaunchedEffect(reorderableState.isAnyItemDragging) {
|
||||||
if (!reorderableState.isAnyItemDragging) {
|
if (!reorderableState.isAnyItemDragging) {
|
||||||
viewModel.saveSortOrder()
|
onIntent(DictRuleIntent.SaveSortOrder)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +219,7 @@ fun DictRuleScreen(
|
|||||||
title = stringResource(R.string.delete),
|
title = stringResource(R.string.delete),
|
||||||
confirmText = stringResource(R.string.ok),
|
confirmText = stringResource(R.string.ok),
|
||||||
onConfirm = { rule ->
|
onConfirm = { rule ->
|
||||||
viewModel.delete(rule)
|
onIntent(DictRuleIntent.DeleteRule(rule))
|
||||||
showDeleteRuleDialog = null
|
showDeleteRuleDialog = null
|
||||||
},
|
},
|
||||||
dismissText = stringResource(R.string.cancel),
|
dismissText = stringResource(R.string.cancel),
|
||||||
@@ -215,16 +237,12 @@ fun DictRuleScreen(
|
|||||||
editingRule = null
|
editingRule = null
|
||||||
},
|
},
|
||||||
onSave = { updatedRule ->
|
onSave = { updatedRule ->
|
||||||
if (editingRule == null) {
|
onIntent(DictRuleIntent.SaveRule(updatedRule, isNew = editingRule == null))
|
||||||
viewModel.insert(updatedRule)
|
|
||||||
} else {
|
|
||||||
viewModel.update(updatedRule)
|
|
||||||
}
|
|
||||||
showEditSheet = false
|
showEditSheet = false
|
||||||
editingRule = null
|
editingRule = null
|
||||||
},
|
},
|
||||||
onCopy = { viewModel.copyRule(it) },
|
onCopy = { onIntent(DictRuleIntent.CopyRule(it)) },
|
||||||
onPaste = { viewModel.pasteRule() },
|
onPaste = onPasteRule,
|
||||||
toFields = { r ->
|
toFields = { r ->
|
||||||
RuleEditFields(
|
RuleEditFields(
|
||||||
name = r?.name ?: "",
|
name = r?.name ?: "",
|
||||||
@@ -247,27 +265,24 @@ fun DictRuleScreen(
|
|||||||
|
|
||||||
RuleListScaffold(
|
RuleListScaffold(
|
||||||
title = "字典规则",
|
title = "字典规则",
|
||||||
state = uiState,
|
state = state,
|
||||||
onBackClick = { onBackClick() },
|
onBackClick = { onBackClick() },
|
||||||
onSearchToggle = { active ->
|
onSearchToggle = { active ->
|
||||||
viewModel.setSearchMode(active)
|
onIntent(DictRuleIntent.SetSearchMode(active))
|
||||||
},
|
},
|
||||||
onSearchQueryChange = { viewModel.setSearchKey(it) },
|
onSearchQueryChange = { onIntent(DictRuleIntent.UpdateSearchQuery(it)) },
|
||||||
searchPlaceholder = stringResource(R.string.replace_purify_search),
|
searchPlaceholder = stringResource(R.string.replace_purify_search),
|
||||||
onClearSelection = { viewModel.setSelection(emptySet()) },
|
onClearSelection = { onIntent(DictRuleIntent.ClearSelection) },
|
||||||
onSelectAll = { viewModel.setSelection(rules.map { it.id }.toSet()) },
|
onSelectAll = { onIntent(DictRuleIntent.SelectAll) },
|
||||||
onSelectInvert = {
|
onSelectInvert = {
|
||||||
val allIds = rules.map { it.id }.toSet()
|
onIntent(DictRuleIntent.InvertSelection)
|
||||||
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(DictRuleIntent.EnableSelection)
|
||||||
viewModel.setSelection(emptySet())
|
|
||||||
}),
|
}),
|
||||||
ActionItem(text = stringResource(R.string.disable_selection), onClick = {
|
ActionItem(text = stringResource(R.string.disable_selection), onClick = {
|
||||||
viewModel.disableSelectionByIds(selectedIds)
|
onIntent(DictRuleIntent.DisableSelection)
|
||||||
viewModel.setSelection(emptySet())
|
|
||||||
}),
|
}),
|
||||||
ActionItem(
|
ActionItem(
|
||||||
text = stringResource(R.string.export),
|
text = stringResource(R.string.export),
|
||||||
@@ -275,8 +290,8 @@ fun DictRuleScreen(
|
|||||||
),
|
),
|
||||||
onDeleteSelected = { ids ->
|
onDeleteSelected = { ids ->
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
viewModel.delSelectionByIds(ids as Set<String>)
|
onIntent(DictRuleIntent.SetSelection(ids as Set<String>))
|
||||||
viewModel.setSelection(emptySet())
|
onIntent(DictRuleIntent.DeleteSelection)
|
||||||
},
|
},
|
||||||
onAddClick = {
|
onAddClick = {
|
||||||
editingRule = null
|
editingRule = null
|
||||||
@@ -311,8 +326,10 @@ fun DictRuleScreen(
|
|||||||
isEnabled = item.isEnabled,
|
isEnabled = item.isEnabled,
|
||||||
isSelected = selectedIds.contains(item.id),
|
isSelected = selectedIds.contains(item.id),
|
||||||
inSelectionMode = inSelectionMode,
|
inSelectionMode = inSelectionMode,
|
||||||
onToggleSelection = { viewModel.toggleSelection(item.id) },
|
onToggleSelection = { onIntent(DictRuleIntent.ToggleSelection(item.id)) },
|
||||||
onEnabledChange = { enabled -> viewModel.update(item.rule.copy(enabled = enabled)) },
|
onEnabledChange = { enabled ->
|
||||||
|
onIntent(DictRuleIntent.SetRuleEnabled(item.rule, enabled))
|
||||||
|
},
|
||||||
onClickEdit = { editingRule = item.rule; showEditSheet = true },
|
onClickEdit = { editingRule = item.rule; showEditSheet = true },
|
||||||
trailingAction = {
|
trailingAction = {
|
||||||
SmallPlainButton(
|
SmallPlainButton(
|
||||||
@@ -328,7 +345,7 @@ fun DictRuleScreen(
|
|||||||
listState = listState,
|
listState = listState,
|
||||||
items = rules,
|
items = rules,
|
||||||
selectedIds = selectedIds,
|
selectedIds = selectedIds,
|
||||||
onSelectionChange = { viewModel.setSelection(it) },
|
onSelectionChange = { onIntent(DictRuleIntent.SetSelection(it)) },
|
||||||
idProvider = { it.id },
|
idProvider = { it.id },
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxHeight()
|
.fillMaxHeight()
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package io.legado.app.ui.dict.rule
|
package io.legado.app.ui.dict.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.base.BaseRuleViewModel
|
import io.legado.app.base.BaseRuleViewModel
|
||||||
import io.legado.app.data.entities.DictRule
|
import io.legado.app.data.entities.DictRule
|
||||||
@@ -9,8 +8,6 @@ import io.legado.app.data.repository.DictRuleRepository
|
|||||||
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
|
||||||
@@ -19,31 +16,14 @@ import io.legado.app.utils.isJsonArray
|
|||||||
import io.legado.app.utils.isJsonObject
|
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.collections.immutable.toImmutableList
|
||||||
|
import kotlinx.collections.immutable.toImmutableSet
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
@Immutable
|
|
||||||
data class DictRuleItemUi(
|
|
||||||
override val id: String,
|
|
||||||
val urlRule: String,
|
|
||||||
val showRule: String,
|
|
||||||
val isEnabled: Boolean,
|
|
||||||
val rule: DictRule
|
|
||||||
) : SelectableItem<String>
|
|
||||||
|
|
||||||
data class DictRuleUiState(
|
|
||||||
override val items: List<DictRuleItemUi> = emptyList(),
|
|
||||||
override val selectedIds: Set<String> = emptySet(),
|
|
||||||
override val searchKey: String = "",
|
|
||||||
val interaction: InteractionState = InteractionState()
|
|
||||||
) : ListUiState<DictRuleItemUi> {
|
|
||||||
override val isSearch: Boolean get() = interaction.isSearchMode
|
|
||||||
override val isLoading: Boolean get() = interaction.isUploading
|
|
||||||
}
|
|
||||||
|
|
||||||
class DictRuleViewModel(
|
class DictRuleViewModel(
|
||||||
application: Application,
|
application: Application,
|
||||||
uploadRepository: UploadRepository
|
uploadRepository: UploadRepository
|
||||||
@@ -56,9 +36,65 @@ class DictRuleViewModel(
|
|||||||
|
|
||||||
override val rawDataFlow: Flow<List<DictRule>> = repository.flowAll()
|
override val rawDataFlow: Flow<List<DictRule>> = repository.flowAll()
|
||||||
|
|
||||||
override fun filterData(data: List<DictRule>, key: String): List<DictRule> {
|
fun onIntent(intent: DictRuleIntent) {
|
||||||
val filtered = if (key.isEmpty()) data
|
when (intent) {
|
||||||
else data.filter { it.name.contains(key, ignoreCase = true) }
|
is DictRuleIntent.SetSearchMode -> setSearchMode(intent.active)
|
||||||
|
is DictRuleIntent.UpdateSearchQuery -> setSearchKey(intent.query)
|
||||||
|
DictRuleIntent.ClearSelection -> setSelection(emptySet())
|
||||||
|
DictRuleIntent.SelectAll -> selectAll()
|
||||||
|
DictRuleIntent.InvertSelection -> invertSelection()
|
||||||
|
is DictRuleIntent.SetSelection -> setSelection(intent.ids)
|
||||||
|
is DictRuleIntent.ToggleSelection -> toggleSelection(intent.id)
|
||||||
|
DictRuleIntent.EnableSelection -> {
|
||||||
|
enableSelectionByIds(uiState.value.selectedIds)
|
||||||
|
setSelection(emptySet())
|
||||||
|
}
|
||||||
|
DictRuleIntent.DisableSelection -> {
|
||||||
|
disableSelectionByIds(uiState.value.selectedIds)
|
||||||
|
setSelection(emptySet())
|
||||||
|
}
|
||||||
|
DictRuleIntent.DeleteSelection -> {
|
||||||
|
delSelectionByIds(uiState.value.selectedIds)
|
||||||
|
setSelection(emptySet())
|
||||||
|
}
|
||||||
|
DictRuleIntent.UploadSelection -> {
|
||||||
|
val state = uiState.value
|
||||||
|
uploadSelectedRules(state.selectedIds, state.items)
|
||||||
|
}
|
||||||
|
is DictRuleIntent.ExportSelection -> {
|
||||||
|
val state = uiState.value
|
||||||
|
exportToUri(intent.uri, state.items, state.selectedIds)
|
||||||
|
}
|
||||||
|
is DictRuleIntent.MoveItem -> moveItemInList(intent.from, intent.to)
|
||||||
|
DictRuleIntent.SaveSortOrder -> saveSortOrder()
|
||||||
|
is DictRuleIntent.SaveRule -> {
|
||||||
|
if (intent.isNew) {
|
||||||
|
insert(intent.rule)
|
||||||
|
} else {
|
||||||
|
update(intent.rule)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is DictRuleIntent.DeleteRule -> delete(intent.rule)
|
||||||
|
is DictRuleIntent.SetRuleEnabled -> update(intent.rule.copy(enabled = intent.enabled))
|
||||||
|
is DictRuleIntent.CopyRule -> copyRule(intent.rule)
|
||||||
|
is DictRuleIntent.ImportSource -> importSource(intent.text)
|
||||||
|
DictRuleIntent.CancelImport -> cancelImport()
|
||||||
|
is DictRuleIntent.ToggleImportSelection -> toggleImportSelection(intent.index)
|
||||||
|
is DictRuleIntent.ToggleImportAll -> toggleImportAll(intent.isSelected)
|
||||||
|
is DictRuleIntent.UpdateImportItem -> updateImportItem(intent.index, intent.rule)
|
||||||
|
DictRuleIntent.SaveImportedRules -> saveImportedRules()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun filterData(
|
||||||
|
data: List<DictRule>,
|
||||||
|
searchKey: String,
|
||||||
|
groupFilter: String
|
||||||
|
): List<DictRule> {
|
||||||
|
val key = groupFilter.ifEmpty { searchKey }
|
||||||
|
val filtered = if (key.isEmpty()) data else {
|
||||||
|
data.filter { it.name.contains(key, ignoreCase = true) }
|
||||||
|
}
|
||||||
return filtered.sortedBy { it.sortNumber }
|
return filtered.sortedBy { it.sortNumber }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,8 +106,8 @@ class DictRuleViewModel(
|
|||||||
importState: BaseImportUiState<DictRule>
|
importState: BaseImportUiState<DictRule>
|
||||||
): DictRuleUiState {
|
): DictRuleUiState {
|
||||||
return DictRuleUiState(
|
return DictRuleUiState(
|
||||||
items = items,
|
items = items.toImmutableList(),
|
||||||
selectedIds = selectedIds,
|
selectedIds = selectedIds.toImmutableSet(),
|
||||||
searchKey = _searchKey.value,
|
searchKey = _searchKey.value,
|
||||||
interaction = InteractionState(
|
interaction = InteractionState(
|
||||||
isSearchMode = isSearch,
|
isSearchMode = isSearch,
|
||||||
@@ -141,6 +177,15 @@ class DictRuleViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
fun update(vararg rule: DictRule) = viewModelScope.launch { repository.update(*rule) }
|
fun update(vararg rule: DictRule) = viewModelScope.launch { repository.update(*rule) }
|
||||||
fun insert(vararg rule: DictRule) = viewModelScope.launch { repository.insert(*rule) }
|
fun insert(vararg rule: DictRule) = viewModelScope.launch { repository.insert(*rule) }
|
||||||
fun delete(vararg dictRule: DictRule) = viewModelScope.launch { repository.delete(*dictRule) }
|
fun delete(vararg dictRule: DictRule) = viewModelScope.launch { repository.delete(*dictRule) }
|
||||||
|
|||||||
Reference in new issue
Block a user