diff --git a/app/build.gradle b/app/build.gradle index 0b28f1a3e..53a23dfe2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -382,4 +382,6 @@ dependencies { implementation(libs.koin.compose) implementation(libs.koin.compose.viewmodel) + implementation(libs.reorderable) + } diff --git a/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt b/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt index 413db9f4f..e5d535d98 100644 --- a/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt +++ b/app/src/main/java/io/legado/app/data/dao/ReplaceRuleDao.kt @@ -35,16 +35,16 @@ interface ReplaceRuleDao { @Query("SELECT * FROM replace_rules ORDER BY name COLLATE NOCASE DESC") fun flowAllNameDesc(): Flow> - @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> - @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> - @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> - @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> @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> - @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> @Query("SELECT * FROM replace_rules where `group` like :key ORDER BY sortOrder ASC") @@ -186,4 +186,4 @@ interface ReplaceRuleDao { dealGroups(list) }.flowOn(IO) } -} \ No newline at end of file +} diff --git a/app/src/main/java/io/legado/app/data/repository/ReplaceRuleRepository.kt b/app/src/main/java/io/legado/app/data/repository/ReplaceRuleRepository.kt index 383e8355f..d449415be 100644 --- a/app/src/main/java/io/legado/app/data/repository/ReplaceRuleRepository.kt +++ b/app/src/main/java/io/legado/app/data/repository/ReplaceRuleRepository.kt @@ -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) = + suspend fun topByIds(ids: Set, 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, 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) = - 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) { + suspend fun moveOrder(currentRules: List, 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()) } } -} \ No newline at end of file +} diff --git a/app/src/main/java/io/legado/app/ui/book/bookmark/AllBookmarkScreen.kt b/app/src/main/java/io/legado/app/ui/book/bookmark/AllBookmarkScreen.kt index 7952d5a48..2fa6d4bdc 100644 --- a/app/src/main/java/io/legado/app/ui/book/bookmark/AllBookmarkScreen.kt +++ b/app/src/main/java/io/legado/app/ui/book/bookmark/AllBookmarkScreen.kt @@ -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 diff --git a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleScreen.kt b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleScreen.kt index 2ed3430ee..d2bd79a4d 100644 --- a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleScreen.kt +++ b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleScreen.kt @@ -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 ) diff --git a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleViewModel.kt b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleViewModel.kt index f2e892e5b..9f4d9d2f0 100644 --- a/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/replace/ReplaceRuleViewModel.kt @@ -102,34 +102,10 @@ class ReplaceRuleViewModel(application: Application) : BaseViewModel(application baseFlow .map { rules -> val comparator = when (sortMode) { - "asc" -> compareBy { - when (it.order) { - -1 -> Long.MIN_VALUE - -2 -> Long.MAX_VALUE - else -> it.order.toLong() - } - } - "desc" -> compareByDescending { - when (it.order) { - -1 -> Long.MAX_VALUE - -2 -> Long.MIN_VALUE - else -> it.order.toLong() - } - } - "name_asc" -> compareBy { - when (it.order) { - -1 -> Long.MIN_VALUE - -2 -> Long.MAX_VALUE - else -> 0L - } - }.thenBy { it.name.lowercase() } - "name_desc" -> compareBy { - when (it.order) { - -1 -> Long.MIN_VALUE - -2 -> Long.MAX_VALUE - else -> 0L - } - }.thenByDescending { it.name.lowercase() } + "asc" -> compareBy { it.order.toLong() } + "desc" -> compareByDescending { it.order.toLong() } + "name_asc" -> compareBy { it.name.lowercase() } + "name_desc" -> compareByDescending { 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) { execute { - repository.topByIds(ids) + repository.topByIds(ids, _sortMode.value == "desc") } } fun bottomSelectByIds(ids: Set) { 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 } } -} \ No newline at end of file +} diff --git a/app/src/main/java/io/legado/app/ui/widget/components/exportComponents/FilePickerSheet.kt b/app/src/main/java/io/legado/app/ui/widget/components/exportComponents/FilePickerSheet.kt index 300739eac..4fc32392a 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/exportComponents/FilePickerSheet.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/exportComponents/FilePickerSheet.kt @@ -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( diff --git a/app/src/main/java/io/legado/app/ui/widget/components/LazyList.kt b/app/src/main/java/io/legado/app/ui/widget/components/lazylist/LazyList.kt similarity index 98% rename from app/src/main/java/io/legado/app/ui/widget/components/LazyList.kt rename to app/src/main/java/io/legado/app/ui/widget/components/lazylist/LazyList.kt index 01c775ac1..6619c4801 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/LazyList.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/lazylist/LazyList.kt @@ -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 diff --git a/app/src/main/java/io/legado/app/ui/widget/components/VerticalFastScroller.kt b/app/src/main/java/io/legado/app/ui/widget/components/lazylist/VerticalFastScroller.kt similarity index 99% rename from app/src/main/java/io/legado/app/ui/widget/components/VerticalFastScroller.kt rename to app/src/main/java/io/legado/app/ui/widget/components/lazylist/VerticalFastScroller.kt index beae23495..a9d75e7b3 100644 --- a/app/src/main/java/io/legado/app/ui/widget/components/VerticalFastScroller.kt +++ b/app/src/main/java/io/legado/app/ui/widget/components/lazylist/VerticalFastScroller.kt @@ -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 diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index c9baa88ea..5dd6de791 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -42,7 +42,7 @@ 配置替换净化规则 暂无 启用 - 替换净化 - 搜索 + 搜索 书架 收藏夹 收藏 diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 0388136ab..a0bc49067 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -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"]