Skip to content

Commit 58c8051

Browse files
committed
perf: list scroll / appShowInnerDisable
1 parent 42f87bb commit 58c8051

17 files changed

+173
-51
lines changed

app/src/main/kotlin/li/songe/gkd/data/RawSubscription.kt

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,35 @@ data class RawSubscription(
297297
override val apps: List<RawGlobalApp>?,
298298
) : RawGroupProps, RawGlobalRuleProps {
299299
val appIdEnable by lazy {
300-
(apps ?: emptyList()).associate { a -> a.id to (a.enable ?: true) }
300+
if (rules.all { r -> r.apps.isNullOrEmpty() }) {
301+
apps?.associate { a -> a.id to (a.enable ?: true) } ?: emptyMap()
302+
} else {
303+
val allIds = mutableSetOf<String>()
304+
apps?.forEach { a ->
305+
allIds.add(a.id)
306+
}
307+
rules.forEach { r ->
308+
r.apps?.forEach { a ->
309+
allIds.add(a.id)
310+
}
311+
}
312+
val dataMap = mutableMapOf<String, Boolean>()
313+
allIds.forEach forEachId@{ id ->
314+
var temp: Boolean? = null
315+
rules.forEach { r ->
316+
val v = (r.apps ?: apps)?.find { it.id == id }?.enable ?: return@forEachId
317+
if (temp == null) {
318+
temp = v
319+
} else if (temp != v) {
320+
return@forEachId
321+
}
322+
}
323+
if (temp != null) {
324+
dataMap[id] = temp
325+
}
326+
}
327+
dataMap
328+
}
301329
}
302330

303331
override val cacheMap by lazy { HashMap<String, Selector?>() }

app/src/main/kotlin/li/songe/gkd/ui/ActionLogPage.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ import li.songe.gkd.ui.component.waitResult
7070
import li.songe.gkd.ui.style.EmptyHeight
7171
import li.songe.gkd.ui.style.itemHorizontalPadding
7272
import li.songe.gkd.ui.style.scaffoldPadding
73+
import li.songe.gkd.util.LIST_PLACEHOLDER_KEY
7374
import li.songe.gkd.util.LocalMainViewModel
7475
import li.songe.gkd.util.LocalNavController
7576
import li.songe.gkd.util.ProfileTransitions
@@ -180,7 +181,7 @@ fun ActionLogPage(
180181
)
181182
}
182183
}
183-
item {
184+
item(LIST_PLACEHOLDER_KEY) {
184185
Spacer(modifier = Modifier.height(EmptyHeight))
185186
if (actionDataItems.itemCount == 0 && actionDataItems.loadState.refresh !is LoadState.Loading) {
186187
EmptyText(text = "暂无记录")

app/src/main/kotlin/li/songe/gkd/ui/ActivityLogPage.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ import li.songe.gkd.ui.component.waitResult
5252
import li.songe.gkd.ui.style.EmptyHeight
5353
import li.songe.gkd.ui.style.itemHorizontalPadding
5454
import li.songe.gkd.ui.style.scaffoldPadding
55+
import li.songe.gkd.util.LIST_PLACEHOLDER_KEY
5556
import li.songe.gkd.util.LocalNavController
5657
import li.songe.gkd.util.ProfileTransitions
5758
import li.songe.gkd.util.copyText
@@ -120,7 +121,7 @@ fun ActivityLogPage() {
120121
ActivityLogCard(i = i, actionLog = actionLog, lastActionLog = lastActionLog)
121122
}
122123
}
123-
item {
124+
item(LIST_PLACEHOLDER_KEY) {
124125
Spacer(modifier = Modifier.height(EmptyHeight))
125126
if (logCount == 0 && list.loadState.refresh !is LoadState.Loading) {
126127
EmptyText(text = "暂无记录")

app/src/main/kotlin/li/songe/gkd/ui/AppConfigPage.kt

Lines changed: 87 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ package li.songe.gkd.ui
33
import androidx.activity.compose.BackHandler
44
import androidx.compose.animation.AnimatedContent
55
import 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
69
import androidx.compose.foundation.clickable
710
import androidx.compose.foundation.layout.Box
11+
import androidx.compose.foundation.layout.Column
812
import androidx.compose.foundation.layout.Row
913
import androidx.compose.foundation.layout.Spacer
1014
import androidx.compose.foundation.layout.fillMaxWidth
@@ -20,6 +24,7 @@ import androidx.compose.material.icons.automirrored.filled.Sort
2024
import androidx.compose.material.icons.filled.History
2125
import androidx.compose.material.icons.filled.MoreVert
2226
import androidx.compose.material.icons.outlined.Add
27+
import androidx.compose.material3.Checkbox
2328
import androidx.compose.material3.DropdownMenu
2429
import androidx.compose.material3.DropdownMenuItem
2530
import androidx.compose.material3.HorizontalDivider
@@ -43,6 +48,7 @@ import androidx.compose.ui.Alignment
4348
import androidx.compose.ui.Modifier
4449
import androidx.compose.ui.input.nestedscroll.nestedScroll
4550
import androidx.compose.ui.text.style.TextOverflow
51+
import androidx.compose.ui.unit.IntOffset
4652
import androidx.compose.ui.unit.dp
4753
import androidx.lifecycle.viewModelScope
4854
import 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
}

app/src/main/kotlin/li/songe/gkd/ui/AppConfigVm.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class AppConfigVm(stateHandle: SavedStateHandle) : ViewModel() {
4343
val ruleSortTypeFlow =
4444
storeFlow.map(viewModelScope) { RuleSortOption.allSubObject.findOption(it.appRuleSortType) }
4545

46+
val appShowInnerDisableFlow =
47+
storeFlow.map(viewModelScope) { it.appShowInnerDisable }
48+
4649
private val rawGlobalGroups = usedSubsEntriesFlow.map {
4750
it.map { (subsItem, subscription) ->
4851
subscription.globalGroups.map { g ->
@@ -55,7 +58,14 @@ class AppConfigVm(stateHandle: SavedStateHandle) : ViewModel() {
5558
)
5659
}
5760
}.flatten()
61+
}.combine(appShowInnerDisableFlow) { list, show ->
62+
if (show) {
63+
list
64+
} else {
65+
list.filter { g -> g.group.appIdEnable[args.appId] != false }
66+
}
5867
}
68+
5969
private val sortedGlobalGroupsFlow = combine(
6070
rawGlobalGroups,
6171
ruleSortTypeFlow,
@@ -146,6 +156,7 @@ class AppConfigVm(stateHandle: SavedStateHandle) : ViewModel() {
146156
DbSet.categoryConfigDao.queryBySubsIds(subs.map { it.subsItem.id })
147157
}.flatMapLatest { it }.let(linkLoad::invoke)
148158
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
159+
149160
val appGroupsFlow = combine(
150161
sortedAppGroupsFlow,
151162
appConfigsFlow,

app/src/main/kotlin/li/songe/gkd/ui/GlobalGroupExcludePage.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import li.songe.gkd.ui.style.EmptyHeight
7373
import li.songe.gkd.ui.style.itemFlagPadding
7474
import li.songe.gkd.ui.style.menuPadding
7575
import li.songe.gkd.ui.style.scaffoldPadding
76+
import li.songe.gkd.util.LIST_PLACEHOLDER_KEY
7677
import li.songe.gkd.util.LocalNavController
7778
import li.songe.gkd.util.ProfileTransitions
7879
import li.songe.gkd.util.SafeR
@@ -125,9 +126,13 @@ fun GlobalGroupExcludePage(subsItemId: Long, groupKey: Int) {
125126
}
126127
}
127128
var expanded by remember { mutableStateOf(false) }
129+
val softwareKeyboardController = LocalSoftwareKeyboardController.current
128130
Scaffold(modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), topBar = {
129131
TopAppBar(scrollBehavior = scrollBehavior, navigationIcon = {
130-
IconButton(onClick = {
132+
IconButton(onClick = throttle {
133+
if (KeyboardUtils.isSoftInputVisible(context)) {
134+
softwareKeyboardController?.hide()
135+
}
131136
navController.popBackStack()
132137
}) {
133138
Icon(
@@ -137,7 +142,6 @@ fun GlobalGroupExcludePage(subsItemId: Long, groupKey: Int) {
137142
}
138143
}, title = {
139144
if (showSearchBar) {
140-
val softwareKeyboardController = LocalSoftwareKeyboardController.current
141145
BackHandler {
142146
if (KeyboardUtils.isSoftInputVisible(context)) {
143147
softwareKeyboardController?.hide()
@@ -334,7 +338,7 @@ fun GlobalGroupExcludePage(subsItemId: Long, groupKey: Int) {
334338
CardFlagBar(visible = excludeData.appIds.containsKey(appInfo.id))
335339
}
336340
}
337-
item {
341+
item(LIST_PLACEHOLDER_KEY) {
338342
Spacer(modifier = Modifier.height(EmptyHeight))
339343
if (showAppInfos.isEmpty() && searchStr.isNotEmpty()) {
340344
val hasShowAll = showSystemApp && showHiddenApp

app/src/main/kotlin/li/songe/gkd/ui/GlobalGroupListPage.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import li.songe.gkd.ui.component.waitResult
5353
import li.songe.gkd.ui.icon.BackCloseIcon
5454
import li.songe.gkd.ui.style.EmptyHeight
5555
import li.songe.gkd.ui.style.scaffoldPadding
56+
import li.songe.gkd.util.LIST_PLACEHOLDER_KEY
5657
import li.songe.gkd.util.LocalMainViewModel
5758
import li.songe.gkd.util.LocalNavController
5859
import li.songe.gkd.util.ProfileTransitions
@@ -275,7 +276,7 @@ fun GlobalGroupListPage(subsItemId: Long, @Suppress("unused") focusGroupKey: Int
275276
}
276277
)
277278
}
278-
item {
279+
item(LIST_PLACEHOLDER_KEY) {
279280
Spacer(modifier = Modifier.height(EmptyHeight))
280281
if (globalGroups.isEmpty()) {
281282
EmptyText(text = "暂无规则")

app/src/main/kotlin/li/songe/gkd/ui/SlowGroupPage.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import li.songe.gkd.ui.component.updateDialogOptions
3636
import li.songe.gkd.ui.style.EmptyHeight
3737
import li.songe.gkd.ui.style.itemPadding
3838
import li.songe.gkd.ui.style.scaffoldPadding
39+
import li.songe.gkd.util.LIST_PLACEHOLDER_KEY
3940
import li.songe.gkd.util.LocalMainViewModel
4041
import li.songe.gkd.util.LocalNavController
4142
import li.songe.gkd.util.ProfileTransitions
@@ -90,7 +91,7 @@ fun SlowGroupPage() {
9091
) {
9192
items(
9293
ruleSummary.slowGlobalGroups,
93-
{ (_, r) -> "${r.subsItem.id}-${r.group.key}" }
94+
{ (_, r) -> r.subsItem.id to r.group.key }
9495
) { (group, rule) ->
9596
SlowGroupCard(
9697
modifier = Modifier
@@ -111,7 +112,7 @@ fun SlowGroupPage() {
111112
}
112113
items(
113114
ruleSummary.slowAppGroups,
114-
{ (_, r) -> "${r.subsItem.id}-${r.appId}-${r.group.key}" }
115+
{ (_, r) -> Triple(r.subsItem.id, r.appId, r.group.key) }
115116
) { (group, rule) ->
116117
SlowGroupCard(
117118
modifier = Modifier
@@ -131,7 +132,7 @@ fun SlowGroupPage() {
131132
desc = "${rule.rawSubs.name}/应用规则/${appInfoCache[rule.app.id]?.name ?: rule.app.name ?: rule.app.id}"
132133
)
133134
}
134-
item {
135+
item(LIST_PLACEHOLDER_KEY) {
135136
Spacer(modifier = Modifier.height(EmptyHeight))
136137
if (ruleSummary.slowGroupCount == 0) {
137138
EmptyText(text = "暂无规则")

0 commit comments

Comments
 (0)