@@ -3,8 +3,12 @@ package li.songe.gkd.ui
33import androidx.activity.compose.BackHandler
44import androidx.compose.animation.AnimatedContent
55import androidx.compose.animation.core.AnimationConstants
6+ import androidx.compose.animation.core.Spring
7+ import androidx.compose.animation.core.VisibilityThreshold
8+ import androidx.compose.animation.core.spring
69import androidx.compose.foundation.clickable
710import androidx.compose.foundation.layout.Box
11+ import androidx.compose.foundation.layout.Column
812import androidx.compose.foundation.layout.Row
913import androidx.compose.foundation.layout.Spacer
1014import androidx.compose.foundation.layout.fillMaxWidth
@@ -20,6 +24,7 @@ import androidx.compose.material.icons.automirrored.filled.Sort
2024import androidx.compose.material.icons.filled.History
2125import androidx.compose.material.icons.filled.MoreVert
2226import androidx.compose.material.icons.outlined.Add
27+ import androidx.compose.material3.Checkbox
2328import androidx.compose.material3.DropdownMenu
2429import androidx.compose.material3.DropdownMenuItem
2530import androidx.compose.material3.HorizontalDivider
@@ -43,6 +48,7 @@ import androidx.compose.ui.Alignment
4348import androidx.compose.ui.Modifier
4449import androidx.compose.ui.input.nestedscroll.nestedScroll
4550import androidx.compose.ui.text.style.TextOverflow
51+ import androidx.compose.ui.unit.IntOffset
4652import androidx.compose.ui.unit.dp
4753import androidx.lifecycle.viewModelScope
4854import androidx.lifecycle.viewmodel.compose.viewModel
@@ -98,7 +104,7 @@ fun AppConfigPage(appId: String) {
98104 if (isFirstVisit) {
99105 isFirstVisit = false
100106 } else {
101- listState.scrollToItem (0 )
107+ listState.animateScrollToItem (0 )
102108 }
103109 }
104110 val isSelectedMode = vm.isSelectedModeFlow.collectAsState().value
@@ -228,16 +234,39 @@ fun AppConfigPage(appId: String) {
228234 trailingIcon = {
229235 RadioButton (
230236 selected = ruleSortType == s,
231- onClick = {
237+ onClick = throttle {
232238 storeFlow.update { it.copy(appRuleSortType = s.value) }
233239 }
234240 )
235241 },
236- onClick = {
242+ onClick = throttle {
237243 storeFlow.update { it.copy(appRuleSortType = s.value) }
238244 },
239245 )
240246 }
247+ Text (
248+ text = " 筛选" ,
249+ modifier = Modifier .menuPadding(),
250+ style = MaterialTheme .typography.labelMedium,
251+ color = MaterialTheme .colorScheme.primary,
252+ )
253+ DropdownMenuItem (
254+ text = {
255+ Text (" 显示禁用规则" )
256+ },
257+ trailingIcon = {
258+ val appShowInnerDisable by vm.appShowInnerDisableFlow.collectAsState()
259+ Checkbox (
260+ checked = appShowInnerDisable,
261+ onCheckedChange = throttle<Boolean > {
262+ storeFlow.update { s -> s.copy(appShowInnerDisable = ! s.appShowInnerDisable) }
263+ }
264+ )
265+ },
266+ onClick = throttle {
267+ storeFlow.update { s -> s.copy(appShowInnerDisable = ! s.appShowInnerDisable) }
268+ },
269+ )
241270 }
242271 }
243272 }
@@ -279,7 +308,12 @@ fun AppConfigPage(appId: String) {
279308 modifier = Modifier .scaffoldPadding(contentPadding),
280309 state = listState,
281310 ) {
282- itemsIndexed(globalGroups) { i, g ->
311+ itemsIndexed(
312+ globalGroups,
313+ { _, g ->
314+ Pair (g.subscription.id, g.group.key)
315+ }
316+ ) { i, g ->
283317 TitleGroupCard (globalGroups, i, isSelectedMode) {
284318 RuleGroupCard (
285319 subs = g.subscription,
@@ -296,14 +330,19 @@ fun AppConfigPage(appId: String) {
296330 )
297331 }
298332 }
299- item {
300- if (globalGroups.isNotEmpty() && appGroups.isNotEmpty() ) {
333+ if (globalGroups.isNotEmpty() && appGroups.isNotEmpty()) {
334+ item( " divider " ) {
301335 HorizontalDivider (
302336 modifier = Modifier .padding(vertical = 8 .dp)
303337 )
304338 }
305339 }
306- itemsIndexed(appGroups) { i, g ->
340+ itemsIndexed(
341+ appGroups,
342+ { _, g ->
343+ Triple (g.subscription.id, g.appId, g.group.key)
344+ }
345+ ) { i, g ->
307346 TitleGroupCard (appGroups, i, isSelectedMode) {
308347 RuleGroupCard (
309348 subs = g.subscription,
@@ -342,31 +381,47 @@ private fun LazyItemScope.TitleGroupCard(
342381) {
343382 val lastGroup = groups.getOrNull(i - 1 )
344383 val g = groups[i]
345- if (g.subsItem.id != lastGroup?.subsItem?.id) {
346- val navController = LocalNavController .current
347- Text (
348- text = g.subscription.name,
349- modifier = Modifier
350- .titleItemPadding(showTop = i > 0 )
351- .run {
352- if (g is ResolvedAppGroup && ! isSelectedMode) {
353- clickable(onClick = throttle {
354- navController.toDestinationsNavigator()
355- .navigate(SubsAppGroupListPageDestination (g.subsItem.id, g.app.id))
356- })
357- } else {
358- this
384+ Column (
385+ modifier = Modifier .animateItem(
386+ fadeInSpec = spring(stiffness = Spring .StiffnessMediumLow ),
387+ placementSpec = spring(
388+ stiffness = Spring .StiffnessMediumLow ,
389+ visibilityThreshold = IntOffset .VisibilityThreshold
390+ ),
391+ fadeOutSpec = spring(stiffness = Spring .StiffnessMediumLow )
392+ ),
393+ ) {
394+ if (g.subsItem.id != lastGroup?.subsItem?.id) {
395+ val navController = LocalNavController .current
396+ Text (
397+ text = g.subscription.name,
398+ modifier = Modifier
399+ .titleItemPadding(showTop = i > 0 )
400+ .run {
401+ if (g is ResolvedAppGroup && ! isSelectedMode) {
402+ clickable(onClick = throttle {
403+ navController.toDestinationsNavigator()
404+ .navigate(
405+ SubsAppGroupListPageDestination (
406+ g.subsItem.id,
407+ g.app.id
408+ )
409+ )
410+ })
411+ } else {
412+ this
413+ }
359414 }
360- }
361- .fillMaxWidth() ,
362- style = MaterialTheme .typography.titleSmall ,
363- color = MaterialTheme .colorScheme.primary ,
364- maxLines = 1 ,
365- softWrap = false ,
366- overflow = TextOverflow . Ellipsis ,
367- )
368- content()
369- } else {
370- content()
415+ .fillMaxWidth(),
416+ style = MaterialTheme .typography.titleSmall ,
417+ color = MaterialTheme .colorScheme.primary ,
418+ maxLines = 1 ,
419+ softWrap = false ,
420+ overflow = TextOverflow . Ellipsis ,
421+ )
422+ content( )
423+ } else {
424+ content()
425+ }
371426 }
372427}
0 commit comments