Skip to content

Commit 0efa7d4

Browse files
committed
review: review fixes
1 parent 9cc897b commit 0efa7d4

3 files changed

Lines changed: 64 additions & 42 deletions

File tree

plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/KonfeatureScreen.kt

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import androidx.compose.foundation.layout.Column
77
import androidx.compose.foundation.layout.Row
88
import androidx.compose.foundation.layout.Spacer
99
import androidx.compose.foundation.layout.fillMaxWidth
10-
import androidx.compose.foundation.layout.height
1110
import androidx.compose.foundation.layout.padding
1211
import androidx.compose.foundation.lazy.LazyColumn
1312
import androidx.compose.material.Button
@@ -141,13 +140,11 @@ private fun KonfeatureHeader(
141140
.background(colorResource(id = CoreR.color.super_light_gray))
142141
.padding(horizontal = 16.dp)
143142
) {
144-
Spacer(modifier = Modifier.height(8.dp))
145143
KonfeatureSearchBar(
146144
query = searchQuery,
147145
onQueryChange = onSearchQueryChange,
148-
modifier = Modifier
146+
modifier = Modifier.padding(vertical = 8.dp)
149147
)
150-
Spacer(modifier = Modifier.height(8.dp))
151148
Row {
152149
Button(onClick = onRefreshClick) {
153150
Text(text = stringResource(id = R.string.konfeature_plugin_refresh))

plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/KonfeatureViewModel.kt

Lines changed: 61 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,28 +12,31 @@ import com.redmadrobot.konfeature.FeatureValueSpec
1212
import com.redmadrobot.konfeature.Konfeature
1313
import com.redmadrobot.konfeature.source.FeatureValueSource
1414
import kotlinx.coroutines.Dispatchers
15+
import kotlinx.coroutines.FlowPreview
1516
import kotlinx.coroutines.flow.Flow
1617
import kotlinx.coroutines.flow.MutableStateFlow
1718
import kotlinx.coroutines.flow.asStateFlow
19+
import kotlinx.coroutines.flow.debounce
1820
import kotlinx.coroutines.flow.launchIn
1921
import kotlinx.coroutines.flow.onEach
2022
import kotlinx.coroutines.flow.update
2123
import kotlinx.coroutines.launch
2224
import kotlinx.coroutines.withContext
2325

26+
private const val SEARCH_QUERY_DELAY_MILLIS = 500L
27+
2428
internal class KonfeatureViewModel(
2529
private val konfeature: Konfeature,
2630
private val debugPanelInterceptor: KonfeatureDebugPanelInterceptor,
2731
) : PluginViewModel() {
2832
private val _state = MutableStateFlow(KonfeatureViewState())
33+
private val _searchQueryFlow = MutableStateFlow("")
2934

3035
val state: Flow<KonfeatureViewState> = _state.asStateFlow()
3136

3237
init {
33-
debugPanelInterceptor
34-
.valuesFlow
35-
.onEach { updateItems() }
36-
.launchIn(viewModelScope)
38+
observeKonfeatureValues()
39+
observeSearchQuery()
3740
}
3841

3942
fun onValueChanged(key: String, value: Any) {
@@ -70,14 +73,7 @@ internal class KonfeatureViewModel(
7073
}
7174

7275
fun onCollapseAllClick() {
73-
_state.update { state ->
74-
val collapsedConfigs = state.items
75-
.asSequence()
76-
.filterIsInstance(KonfeatureItem.Config::class.java)
77-
.map { it.name }
78-
.toSet()
79-
state.copy(collapsedConfigs = collapsedConfigs)
80-
}
76+
_state.update { state -> state.copy(collapsedConfigs = state.configs.keys) }
8177
}
8278

8379
fun onEditClick(key: String, value: Any, isDebugSource: Boolean) {
@@ -89,33 +85,56 @@ internal class KonfeatureViewModel(
8985
}
9086

9187
fun onSearchQueryChanged(query: String) {
92-
_state.update { state ->
93-
val filteredItems = filterItems(state.items, query)
94-
state.copy(searchQuery = query, filteredItems = filteredItems)
95-
}
88+
_state.update { state -> state.copy(searchQuery = query) }
89+
_searchQueryFlow.update { query }
90+
}
91+
92+
private fun observeKonfeatureValues() {
93+
debugPanelInterceptor.valuesFlow
94+
.onEach { updateItems() }
95+
.launchIn(viewModelScope)
96+
}
97+
98+
@OptIn(FlowPreview::class)
99+
private fun observeSearchQuery() {
100+
_searchQueryFlow
101+
.debounce(timeoutMillis = SEARCH_QUERY_DELAY_MILLIS)
102+
.onEach { query ->
103+
_state.update { state ->
104+
state.copy(filteredItems = filterItems(state.configs, state.values, query))
105+
}
106+
}
107+
.launchIn(viewModelScope)
96108
}
97109

98110
private suspend fun updateItems() {
99-
val items = withContext(Dispatchers.IO) { getItems(konfeature) }
111+
val (configs, values) = withContext(Dispatchers.IO) { getItems(konfeature) }
112+
val searchQuery = _searchQueryFlow.value
113+
val filteredItems = filterItems(configs, values, searchQuery)
114+
100115
_state.update { state ->
101-
val filteredItems = filterItems(items, state.searchQuery)
102-
state.copy(items = items, filteredItems = filteredItems)
116+
state.copy(configs = configs, values = values, filteredItems = filteredItems)
103117
}
104118
}
105119

106-
private fun getItems(konfeature: Konfeature): List<KonfeatureItem> {
107-
return konfeature.spec.fold(mutableListOf<KonfeatureItem>()) { acc, configSpec ->
120+
private fun getItems(konfeature: Konfeature): Pair<Map<String, KonfeatureItem.Config>, List<KonfeatureItem.Value>> {
121+
val configs = mutableMapOf<String, KonfeatureItem.Config>()
122+
val values = mutableListOf<KonfeatureItem.Value>()
123+
124+
konfeature.spec.fold(configs to values) { acc, configSpec ->
108125
acc.apply {
109-
add(createConfigItem(configSpec))
110-
addAll(configSpec.values.map { valueSpec ->
126+
configs[configSpec.name] = createConfigItem(configSpec)
127+
configSpec.values.mapTo(values) { valueSpec ->
111128
createConfigValueItem(
112129
configName = configSpec.name,
113130
valueSpec = valueSpec,
114131
konfeature = konfeature
115132
)
116-
})
133+
}
117134
}
118135
}
136+
137+
return configs to values
119138
}
120139

121140
private fun createConfigItem(config: FeatureConfigSpec): KonfeatureItem.Config {
@@ -162,19 +181,24 @@ internal class KonfeatureViewModel(
162181
}
163182
}
164183

165-
private fun filterItems(items: List<KonfeatureItem>, query: String): List<KonfeatureItem> {
166-
if (query.isBlank()) return items
167-
168-
val formattedQuery = query.lowercase()
169-
val matchingItems = items
170-
.filterIsInstance<KonfeatureItem.Value>()
171-
.filter { it.key.lowercase().contains(formattedQuery) }
172-
val matchingConfigNames = matchingItems.map { it.configName }.toSet()
173-
174-
return items.filter { item ->
175-
when (item) {
176-
is KonfeatureItem.Config -> item.name in matchingConfigNames
177-
is KonfeatureItem.Value -> item.key.lowercase().contains(formattedQuery)
184+
private suspend fun filterItems(
185+
configs: Map<String, KonfeatureItem.Config>,
186+
values: List<KonfeatureItem.Value>,
187+
query: String
188+
): List<KonfeatureItem> {
189+
return withContext(Dispatchers.Default) {
190+
buildList {
191+
var previousValue: KonfeatureItem.Value? = null
192+
193+
for (value in values) {
194+
if (value.key.contains(query, ignoreCase = true)) {
195+
if (previousValue?.configName != value.configName) {
196+
configs[value.configName]?.let { config -> add(config) }
197+
}
198+
add(value)
199+
previousValue = value
200+
}
201+
}
178202
}
179203
}
180204
}

plugins/plugin-konfeature/src/main/kotlin/com/redmadrobot/debug/plugin/konfeature/ui/data/KonfeatureViewState.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ package com.redmadrobot.debug.plugin.konfeature.ui.data
33
internal data class KonfeatureViewState(
44
val searchQuery: String = "",
55
val collapsedConfigs: Set<String> = emptySet(),
6-
val items: List<KonfeatureItem> = emptyList(),
6+
val configs: Map<String, KonfeatureItem.Config> = emptyMap(),
7+
val values: List<KonfeatureItem.Value> = emptyList(),
78
val filteredItems: List<KonfeatureItem> = emptyList(),
89
val editDialogState: EditDialogState? = null
910
) {

0 commit comments

Comments
 (0)