[优化] 优化了替换净化置顶置底逻辑,回归了时间排序下的拖动排序
This commit is contained in:
@@ -382,4 +382,6 @@ dependencies {
|
||||
implementation(libs.koin.compose)
|
||||
implementation(libs.koin.compose.viewmodel)
|
||||
|
||||
implementation(libs.reorderable)
|
||||
|
||||
}
|
||||
|
||||
@@ -35,16 +35,16 @@ interface ReplaceRuleDao {
|
||||
@Query("SELECT * FROM replace_rules ORDER BY name COLLATE NOCASE DESC")
|
||||
fun flowAllNameDesc(): Flow<List<ReplaceRule>>
|
||||
|
||||
@Query("SELECT * FROM replace_rules WHERE `group` LIKE :key OR name LIKE :key ORDER BY sortOrder ASC")
|
||||
@Query("SELECT * FROM replace_rules WHERE `group` LIKE :key OR name LIKE :key OR pattern LIKE :key OR replacement LIKE :key OR scope LIKE :key ORDER BY sortOrder ASC")
|
||||
fun flowSearchAsc(key: String): Flow<List<ReplaceRule>>
|
||||
|
||||
@Query("SELECT * FROM replace_rules WHERE `group` LIKE :key OR name LIKE :key ORDER BY sortOrder DESC")
|
||||
@Query("SELECT * FROM replace_rules WHERE `group` LIKE :key OR name LIKE :key OR pattern LIKE :key OR replacement LIKE :key OR scope LIKE :key ORDER BY sortOrder DESC")
|
||||
fun flowSearchDesc(key: String): Flow<List<ReplaceRule>>
|
||||
|
||||
@Query("SELECT * FROM replace_rules WHERE `group` LIKE :key OR name LIKE :key ORDER BY name COLLATE NOCASE ASC")
|
||||
@Query("SELECT * FROM replace_rules WHERE `group` LIKE :key OR name LIKE :key OR pattern LIKE :key OR replacement LIKE :key OR scope LIKE :key ORDER BY name COLLATE NOCASE ASC")
|
||||
fun flowSearchNameAsc(key: String): Flow<List<ReplaceRule>>
|
||||
|
||||
@Query("SELECT * FROM replace_rules WHERE `group` LIKE :key OR name LIKE :key ORDER BY name COLLATE NOCASE DESC")
|
||||
@Query("SELECT * FROM replace_rules WHERE `group` LIKE :key OR name LIKE :key OR pattern LIKE :key OR replacement LIKE :key OR scope LIKE :key ORDER BY name COLLATE NOCASE DESC")
|
||||
fun flowSearchNameDesc(key: String): Flow<List<ReplaceRule>>
|
||||
|
||||
@Query("SELECT * FROM replace_rules WHERE `group` LIKE :key ORDER BY sortOrder ASC")
|
||||
@@ -72,7 +72,7 @@ interface ReplaceRuleDao {
|
||||
@Query("SELECT * FROM replace_rules WHERE `group` IS NULL OR trim(`group`) = '' OR trim(`group`) LIKE '%未分组%' ORDER BY name COLLATE NOCASE DESC")
|
||||
fun flowNoGroupNameDesc(): Flow<List<ReplaceRule>>
|
||||
|
||||
@Query("SELECT * FROM replace_rules where `group` like :key or name like :key ORDER BY sortOrder ASC")
|
||||
@Query("SELECT * FROM replace_rules where `group` like :key or name like :key or pattern like :key or replacement like :key or scope like :key ORDER BY sortOrder ASC")
|
||||
fun flowSearch(key: String): Flow<List<ReplaceRule>>
|
||||
|
||||
@Query("SELECT * FROM replace_rules where `group` like :key ORDER BY sortOrder ASC")
|
||||
@@ -186,4 +186,4 @@ interface ReplaceRuleDao {
|
||||
dealGroups(list)
|
||||
}.flowOn(IO)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,16 +45,24 @@ class ReplaceRuleRepository {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun toTop(rule: ReplaceRule) {
|
||||
suspend fun toTop(rule: ReplaceRule, isDesc: Boolean = false) {
|
||||
withContext(Dispatchers.IO) {
|
||||
rule.order = -1
|
||||
if (isDesc) {
|
||||
rule.order = appDb.replaceRuleDao.maxOrder + 1
|
||||
} else {
|
||||
rule.order = appDb.replaceRuleDao.minOrder - 1
|
||||
}
|
||||
appDb.replaceRuleDao.update(rule)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun toBottom(rule: ReplaceRule) {
|
||||
suspend fun toBottom(rule: ReplaceRule, isDesc: Boolean = false) {
|
||||
withContext(Dispatchers.IO) {
|
||||
rule.order = -2
|
||||
if (isDesc) {
|
||||
rule.order = appDb.replaceRuleDao.minOrder - 1
|
||||
} else {
|
||||
rule.order = appDb.replaceRuleDao.maxOrder + 1
|
||||
}
|
||||
appDb.replaceRuleDao.update(rule)
|
||||
}
|
||||
}
|
||||
@@ -154,32 +162,59 @@ class ReplaceRuleRepository {
|
||||
appDb.replaceRuleDao.delete(*rules.toTypedArray())
|
||||
}
|
||||
|
||||
suspend fun topByIds(ids: Set<Long>) =
|
||||
suspend fun topByIds(ids: Set<Long>, isDesc: Boolean = false) =
|
||||
withContext(Dispatchers.IO) {
|
||||
if (ids.isEmpty()) return@withContext
|
||||
val rules = appDb.replaceRuleDao.getByIds(ids)
|
||||
if (isDesc) {
|
||||
var maxOrder = appDb.replaceRuleDao.maxOrder
|
||||
val updated = rules.map {
|
||||
maxOrder++
|
||||
it.copy(order = maxOrder)
|
||||
}
|
||||
appDb.replaceRuleDao.update(*updated.toTypedArray())
|
||||
} else {
|
||||
var minOrder = appDb.replaceRuleDao.minOrder
|
||||
val updated = rules.map {
|
||||
minOrder--
|
||||
it.copy(order = minOrder)
|
||||
}
|
||||
appDb.replaceRuleDao.update(*updated.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun bottomByIds(ids: Set<Long>, isDesc: Boolean = false) =
|
||||
withContext(Dispatchers.IO) {
|
||||
if (ids.isEmpty()) return@withContext
|
||||
|
||||
val rules = appDb.replaceRuleDao.getByIds(ids)
|
||||
val updated = rules.map { it.copy(order = -1) }
|
||||
appDb.replaceRuleDao.update(*updated.toTypedArray())
|
||||
if (isDesc) {
|
||||
var minOrder = appDb.replaceRuleDao.minOrder
|
||||
val updated = rules.map {
|
||||
minOrder--
|
||||
it.copy(order = minOrder)
|
||||
}
|
||||
appDb.replaceRuleDao.update(*updated.toTypedArray())
|
||||
} else {
|
||||
var maxOrder = appDb.replaceRuleDao.maxOrder
|
||||
val updated = rules.map {
|
||||
maxOrder++
|
||||
it.copy(order = maxOrder)
|
||||
}
|
||||
appDb.replaceRuleDao.update(*updated.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun bottomByIds(ids: Set<Long>) =
|
||||
withContext(Dispatchers.IO) {
|
||||
if (ids.isEmpty()) return@withContext
|
||||
|
||||
val rules = appDb.replaceRuleDao.getByIds(ids)
|
||||
val updated = rules.map { it.copy(order = -2) }
|
||||
appDb.replaceRuleDao.update(*updated.toTypedArray())
|
||||
}
|
||||
|
||||
suspend fun moveOrder(currentRules: List<ReplaceRuleItemUi>) {
|
||||
suspend fun moveOrder(currentRules: List<ReplaceRuleItemUi>, isDesc: Boolean = false) {
|
||||
withContext(Dispatchers.IO) {
|
||||
val size = currentRules.size
|
||||
val updatedRules = currentRules.mapIndexed { index, itemUi ->
|
||||
itemUi.rule.copy(order = index + 1)
|
||||
val order = if (isDesc) size - index else index + 1
|
||||
itemUi.rule.copy(order = order)
|
||||
}
|
||||
|
||||
appDb.replaceRuleDao.update(*updatedRules.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
||||
@@ -34,8 +33,8 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import io.legado.app.data.entities.Bookmark
|
||||
import io.legado.app.ui.widget.components.FastScrollLazyColumn
|
||||
import io.legado.app.ui.widget.components.Scroller
|
||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
||||
import io.legado.app.ui.widget.components.lazylist.Scroller
|
||||
import io.legado.app.ui.widget.components.SearchBarSection
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
|
||||
|
||||
@@ -9,12 +9,10 @@ import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.expandHorizontally
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.animation.shrinkHorizontally
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
@@ -43,11 +41,9 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.ClipEntry
|
||||
import androidx.compose.ui.platform.LocalClipboard
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
@@ -59,7 +55,7 @@ import io.legado.app.data.repository.UploadRepository
|
||||
import io.legado.app.ui.replace.edit.ReplaceEditActivity
|
||||
import io.legado.app.ui.widget.components.ActionItem
|
||||
import io.legado.app.ui.widget.components.AnimatedText
|
||||
import io.legado.app.ui.widget.components.FastScrollLazyColumn
|
||||
import io.legado.app.ui.widget.components.lazylist.FastScrollLazyColumn
|
||||
import io.legado.app.ui.widget.components.SearchBarSection
|
||||
import io.legado.app.ui.widget.components.SelectionBottomBar
|
||||
import io.legado.app.ui.widget.components.exportComponents.FilePickerSheet
|
||||
@@ -71,6 +67,8 @@ import kotlinx.coroutines.coroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import org.koin.compose.koinInject
|
||||
import sh.calvin.reorderable.ReorderableItem
|
||||
import sh.calvin.reorderable.rememberReorderableLazyListState
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class,
|
||||
ExperimentalMaterial3ExpressiveApi::class
|
||||
@@ -118,6 +116,16 @@ fun ReplaceRuleScreen(
|
||||
var isUploading by remember { mutableStateOf(false) }
|
||||
val clipboardManager = LocalClipboard.current
|
||||
|
||||
val hapticFeedback = LocalHapticFeedback.current
|
||||
val reorderableState = rememberReorderableLazyListState(listState) { from, to ->
|
||||
viewModel.moveItemInList(from.index, to.index)
|
||||
hapticFeedback.performHapticFeedback(HapticFeedbackType.SegmentFrequentTick)
|
||||
}
|
||||
|
||||
val canReorder = remember(uiState.sortMode) {
|
||||
uiState.sortMode == "asc" || uiState.sortMode == "desc"
|
||||
}
|
||||
|
||||
val importDoc = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.OpenDocument(),
|
||||
onResult = { uri ->
|
||||
@@ -177,11 +185,9 @@ fun ReplaceRuleScreen(
|
||||
.map { it.rule }
|
||||
val json = Gson().toJson(rulesToExport)
|
||||
|
||||
// [新增] 设置加载状态
|
||||
isUploading = true
|
||||
|
||||
try {
|
||||
// 使用 runCatching 处理上传
|
||||
runCatching {
|
||||
uploadRepository.upload(
|
||||
fileName = "exportReplaceRule.json",
|
||||
@@ -246,6 +252,12 @@ fun ReplaceRuleScreen(
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(reorderableState.isAnyItemDragging) {
|
||||
if (!reorderableState.isAnyItemDragging) {
|
||||
viewModel.saveSortOrder()
|
||||
}
|
||||
}
|
||||
|
||||
if (showGroupManageSheet) {
|
||||
GroupManageBottomSheet(
|
||||
groups = groups,
|
||||
@@ -386,11 +398,23 @@ fun ReplaceRuleScreen(
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("名称升序") },
|
||||
onClick = { viewModel.setSortMode("name_asc"); showMenu = false }
|
||||
onClick = {
|
||||
viewModel.setSortMode("name_asc")
|
||||
showMenu = false
|
||||
scope.launch {
|
||||
snackbarHostState.showSnackbar("非时间排序模式下将禁用拖动")
|
||||
}
|
||||
}
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text("名称降序") },
|
||||
onClick = { viewModel.setSortMode("name_desc"); showMenu = false }
|
||||
onClick = {
|
||||
viewModel.setSortMode("name_desc")
|
||||
showMenu = false
|
||||
scope.launch {
|
||||
snackbarHostState.showSnackbar("非时间排序模式下将禁用拖动")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -432,7 +456,7 @@ fun ReplaceRuleScreen(
|
||||
text = title,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.padding(horizontal = 12.dp),
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
@@ -473,30 +497,43 @@ fun ReplaceRuleScreen(
|
||||
) {
|
||||
items(rules, key = { it.id }) { ui ->
|
||||
val isSelected = selectedRuleIds.contains(ui.id)
|
||||
|
||||
ReplaceRuleItem(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 12.dp)
|
||||
.animateItem(),
|
||||
name = ui.name,
|
||||
isEnabled = ui.isEnabled,
|
||||
isSelected = isSelected,
|
||||
inSelectionMode = inSelectionMode,
|
||||
onEnabledChange = { enabled ->
|
||||
viewModel.update(ui.rule.copy(isEnabled = enabled))
|
||||
},
|
||||
onDelete = { showDeleteRuleDialog = ui.rule },
|
||||
onToTop = { viewModel.toTop(ui.rule) },
|
||||
onToBottom = { viewModel.toBottom(ui.rule) },
|
||||
onToggleSelection = {
|
||||
viewModel.toggleSelection(ui.id)
|
||||
},
|
||||
onClickEdit = {
|
||||
context.startActivity(
|
||||
ReplaceEditActivity.startIntent(context, ui.id)
|
||||
)
|
||||
}
|
||||
)
|
||||
ReorderableItem(
|
||||
state = reorderableState,
|
||||
key = ui.id
|
||||
) { _ ->
|
||||
ReplaceRuleItem(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 12.dp)
|
||||
.then(
|
||||
if (canReorder) {
|
||||
Modifier.longPressDraggableHandle(
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
)
|
||||
.animateItem(),
|
||||
name = ui.name,
|
||||
isEnabled = ui.isEnabled,
|
||||
isSelected = isSelected,
|
||||
inSelectionMode = inSelectionMode,
|
||||
onEnabledChange = { enabled ->
|
||||
viewModel.update(ui.rule.copy(isEnabled = enabled))
|
||||
},
|
||||
onDelete = { showDeleteRuleDialog = ui.rule },
|
||||
onToTop = { viewModel.toTop(ui.rule) },
|
||||
onToBottom = { viewModel.toBottom(ui.rule) },
|
||||
onToggleSelection = {
|
||||
viewModel.toggleSelection(ui.id)
|
||||
},
|
||||
onClickEdit = {
|
||||
context.startActivity(
|
||||
ReplaceEditActivity.startIntent(context, ui.id)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (inSelectionMode) {
|
||||
@@ -692,42 +729,65 @@ private fun GroupManageBottomSheet(
|
||||
sheetState = sheetState
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(bottom = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Text(stringResource(R.string.group_manage), style = MaterialTheme.typography.titleLarge)
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 16.dp),
|
||||
text = stringResource(R.string.group_manage),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
)
|
||||
LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
items(groups) { group ->
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Card(modifier = Modifier.fillMaxWidth()) {
|
||||
if (editingGroup == group) {
|
||||
OutlinedTextField(
|
||||
value = updatedGroupName,
|
||||
onValueChange = { updatedGroupName = it },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
IconButton(onClick = {
|
||||
viewModel.upGroup(group, updatedGroupName)
|
||||
editingGroup = null
|
||||
}) {
|
||||
Icon(Icons.Default.Check, contentDescription = stringResource(id = R.string.ok))
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = updatedGroupName,
|
||||
onValueChange = { updatedGroupName = it },
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
IconButton(onClick = {
|
||||
viewModel.upGroup(group, updatedGroupName)
|
||||
editingGroup = null
|
||||
}) {
|
||||
Icon(
|
||||
Icons.Default.Check,
|
||||
contentDescription = stringResource(id = R.string.ok)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Text(group, modifier = Modifier.weight(1f))
|
||||
Row {
|
||||
IconButton(onClick = {
|
||||
editingGroup = group
|
||||
updatedGroupName = group
|
||||
}) {
|
||||
Icon(Icons.Default.Edit, contentDescription = stringResource(id = R.string.edit))
|
||||
ListItem(
|
||||
headlineContent = { Text(group) },
|
||||
trailingContent = {
|
||||
Row {
|
||||
IconButton(onClick = {
|
||||
editingGroup = group
|
||||
updatedGroupName = group
|
||||
}) {
|
||||
Icon(
|
||||
Icons.Default.Edit,
|
||||
contentDescription = stringResource(id = R.string.edit)
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { viewModel.delGroup(group) }) {
|
||||
Icon(
|
||||
Icons.Default.Delete,
|
||||
contentDescription = stringResource(id = R.string.delete)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
IconButton(onClick = { viewModel.delGroup(group) }) {
|
||||
Icon(Icons.Default.Delete, contentDescription = stringResource(id = R.string.delete))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -785,6 +845,7 @@ fun ReplaceRuleItem(
|
||||
AnimatedContent(targetState = name, label = "RuleNameAnimation") { targetName ->
|
||||
Text(
|
||||
text = targetName,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
||||
@@ -102,34 +102,10 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
|
||||
baseFlow
|
||||
.map { rules ->
|
||||
val comparator = when (sortMode) {
|
||||
"asc" -> compareBy<ReplaceRule> {
|
||||
when (it.order) {
|
||||
-1 -> Long.MIN_VALUE
|
||||
-2 -> Long.MAX_VALUE
|
||||
else -> it.order.toLong()
|
||||
}
|
||||
}
|
||||
"desc" -> compareByDescending<ReplaceRule> {
|
||||
when (it.order) {
|
||||
-1 -> Long.MAX_VALUE
|
||||
-2 -> Long.MIN_VALUE
|
||||
else -> it.order.toLong()
|
||||
}
|
||||
}
|
||||
"name_asc" -> compareBy<ReplaceRule> {
|
||||
when (it.order) {
|
||||
-1 -> Long.MIN_VALUE
|
||||
-2 -> Long.MAX_VALUE
|
||||
else -> 0L
|
||||
}
|
||||
}.thenBy { it.name.lowercase() }
|
||||
"name_desc" -> compareBy<ReplaceRule> {
|
||||
when (it.order) {
|
||||
-1 -> Long.MIN_VALUE
|
||||
-2 -> Long.MAX_VALUE
|
||||
else -> 0L
|
||||
}
|
||||
}.thenByDescending { it.name.lowercase() }
|
||||
"asc" -> compareBy<ReplaceRule> { it.order.toLong() }
|
||||
"desc" -> compareByDescending<ReplaceRule> { it.order.toLong() }
|
||||
"name_asc" -> compareBy<ReplaceRule> { it.name.lowercase() }
|
||||
"name_desc" -> compareByDescending<ReplaceRule> { it.name.lowercase() }
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -154,7 +130,7 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
|
||||
_sortMode,
|
||||
_searchKey,
|
||||
repository.flowGroups(),
|
||||
ruleUiFlow
|
||||
_uiRules
|
||||
) { sortMode, searchKey, groups, rules ->
|
||||
ReplaceRuleUiState(
|
||||
sortMode = sortMode,
|
||||
@@ -169,6 +145,14 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
|
||||
initialValue = ReplaceRuleUiState(isLoading = true)
|
||||
)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
ruleUiFlow.collect { rules ->
|
||||
_uiRules.value = rules
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setSortMode(mode: String) {
|
||||
_sortMode.value = mode
|
||||
context.putPrefString(PreferKey.replaceSortMode, mode)
|
||||
@@ -196,13 +180,13 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
|
||||
|
||||
fun toTop(rule: ReplaceRule) {
|
||||
execute {
|
||||
repository.toTop(rule)
|
||||
repository.toTop(rule, _sortMode.value == "desc")
|
||||
}
|
||||
}
|
||||
|
||||
fun toBottom(rule: ReplaceRule) {
|
||||
execute {
|
||||
repository.toBottom(rule)
|
||||
repository.toBottom(rule, _sortMode.value == "desc")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,13 +228,13 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
|
||||
|
||||
fun topSelectByIds(ids: Set<Long>) {
|
||||
execute {
|
||||
repository.topByIds(ids)
|
||||
repository.topByIds(ids, _sortMode.value == "desc")
|
||||
}
|
||||
}
|
||||
|
||||
fun bottomSelectByIds(ids: Set<Long>) {
|
||||
execute {
|
||||
repository.bottomByIds(ids)
|
||||
repository.bottomByIds(ids, _sortMode.value == "desc")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,8 +268,9 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
|
||||
|
||||
fun saveSortOrder() {
|
||||
val currentRules = _uiRules.value
|
||||
val isDesc = _sortMode.value == "desc"
|
||||
execute {
|
||||
repository.moveOrder(currentRules)
|
||||
repository.moveOrder(currentRules, isDesc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,4 +405,4 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -40,14 +40,14 @@ fun FilePickerSheet(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(bottom = 32.dp),
|
||||
.padding(bottom = 16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
|
||||
Text(
|
||||
modifier = Modifier.padding(bottom = 16.dp),
|
||||
text = stringResource(R.string.select_operation),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
modifier = Modifier.padding(bottom = 16.dp)
|
||||
)
|
||||
|
||||
Row(
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package io.legado.app.ui.widget.components
|
||||
package io.legado.app.ui.widget.components.lazylist
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
package io.legado.app.ui.widget.components
|
||||
package io.legado.app.ui.widget.components.lazylist
|
||||
|
||||
import android.view.ViewConfiguration
|
||||
import androidx.compose.animation.core.Animatable
|
||||
@@ -46,7 +46,7 @@ import androidx.compose.ui.util.fastFirstOrNull
|
||||
import androidx.compose.ui.util.fastForEach
|
||||
import androidx.compose.ui.util.fastLastOrNull
|
||||
import androidx.compose.ui.util.fastMaxBy
|
||||
import io.legado.app.ui.widget.components.Scroller.STICKY_HEADER_KEY_PREFIX
|
||||
import io.legado.app.ui.widget.components.lazylist.Scroller.STICKY_HEADER_KEY_PREFIX
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
@@ -42,7 +42,7 @@
|
||||
<string name="replace_purify_desc">配置替换净化规则</string>
|
||||
<string name="not_available">暂无</string>
|
||||
<string name="enable">启用</string>
|
||||
<string name="replace_purify_search">替换净化 - 搜索</string>
|
||||
<string name="replace_purify_search">搜索</string>
|
||||
<string name="bookshelf">书架</string>
|
||||
<string name="favorites">收藏夹</string>
|
||||
<string name="favorite">收藏</string>
|
||||
|
||||
@@ -72,6 +72,8 @@ zxingLite = "3.3.0"
|
||||
|
||||
koin-bom = "4.1.1"
|
||||
biometricKtx = "1.1.0"
|
||||
|
||||
reorderable = "3.0.0"
|
||||
[libraries]
|
||||
|
||||
accompanist-webview = { module = "com.google.accompanist:accompanist-webview", version.ref = "accompanistWebview" }
|
||||
@@ -214,6 +216,8 @@ renderscript-intrinsics-replacement-toolkit = { module = "com.github.TomasValent
|
||||
zxing-lite = { module = "com.github.jenly1314:zxing-lite", version.ref = "zxingLite" }
|
||||
androidx-biometric-ktx = { group = "androidx.biometric", name = "biometric-ktx", version.ref = "biometricKtx" }
|
||||
|
||||
reorderable = { module = "sh.calvin.reorderable:reorderable", version.ref = "reorderable" }
|
||||
|
||||
[bundles]
|
||||
coroutines = ["kotlinx-coroutines-core", "kotlinx-coroutines-android"]
|
||||
androidTest = ["androidx-espresso-core", "androidx-junit", "androidx-runner"]
|
||||
|
||||
Reference in New Issue
Block a user