Skip to content

Commit 20b0e27

Browse files
committed
feature: add ability of searching toggles by key
1 parent 0a8aadd commit 20b0e27

4 files changed

Lines changed: 143 additions & 25 deletions

File tree

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

Lines changed: 102 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,20 @@ 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
1011
import androidx.compose.foundation.layout.padding
1112
import androidx.compose.foundation.lazy.LazyColumn
1213
import androidx.compose.material.Button
1314
import androidx.compose.material.Divider
1415
import androidx.compose.material.ExperimentalMaterialApi
1516
import androidx.compose.material.Icon
1617
import androidx.compose.material.IconButton
18+
import androidx.compose.material.OutlinedTextField
1719
import androidx.compose.material.Text
20+
import androidx.compose.material.TextFieldDefaults
1821
import androidx.compose.material.icons.Icons
22+
import androidx.compose.material.icons.filled.Clear
23+
import androidx.compose.material.icons.filled.Search
1924
import androidx.compose.material.icons.outlined.Edit
2025
import androidx.compose.material.icons.outlined.KeyboardArrowDown
2126
import androidx.compose.material.icons.outlined.KeyboardArrowUp
@@ -24,6 +29,7 @@ import androidx.compose.runtime.collectAsState
2429
import androidx.compose.runtime.getValue
2530
import androidx.compose.ui.Alignment
2631
import androidx.compose.ui.Modifier
32+
import androidx.compose.ui.graphics.Color
2733
import androidx.compose.ui.res.colorResource
2834
import androidx.compose.ui.res.stringResource
2935
import androidx.compose.ui.unit.dp
@@ -55,15 +61,16 @@ internal fun KonfeatureScreen(
5561
onResetAllClick = viewModel::onResetAllClick,
5662
onCollapseAllClick = viewModel::onCollapseAllClick,
5763
onHeaderClick = viewModel::onConfigHeaderClick,
58-
onEditClick = viewModel::onEditClick
64+
onEditClick = viewModel::onEditClick,
65+
onSearchQueryChange = viewModel::onSearchQueryChanged,
5966
)
6067

6168
state.editDialogState?.let { dialogState ->
6269
EditConfigValueDialog(
6370
state = dialogState,
6471
onValueChange = viewModel::onValueChanged,
6572
onValueReset = viewModel::onValueReset,
66-
onDismissRequest = viewModel::onEditDialogCloseClik
73+
onDismissRequest = viewModel::onEditDialogCloseClicked
6774
)
6875
}
6976
}
@@ -77,47 +84,121 @@ internal fun KonfeatureLayout(
7784
onCollapseAllClick: () -> Unit,
7885
onResetAllClick: () -> Unit,
7986
onHeaderClick: (String) -> Unit,
87+
onSearchQueryChange: (String) -> Unit,
8088
) {
8189
LazyColumn {
8290
stickyHeader {
83-
Row(
84-
modifier = Modifier
85-
.background(colorResource(id = CoreR.color.super_light_gray))
86-
.padding(horizontal = 16.dp, vertical = 4.dp)
87-
) {
88-
Button(onClick = onRefreshClick) {
89-
Text(text = stringResource(id = R.string.konfeature_plugin_refresh))
90-
}
91-
Spacer(modifier = Modifier.weight(1f))
92-
Button(onClick = onCollapseAllClick) {
93-
Text(text = stringResource(id = R.string.konfeature_plugin_collapse_all))
94-
}
95-
Spacer(modifier = Modifier.weight(1f))
96-
Button(onClick = onResetAllClick) {
97-
Text(text = stringResource(id = R.string.konfeature_plugin_reset_all))
98-
}
91+
KonfeatureHeader(
92+
searchQuery = state.searchQuery,
93+
onSearchQueryChange = onSearchQueryChange,
94+
onRefreshClick = onRefreshClick,
95+
onCollapseAllClick = onCollapseAllClick,
96+
onResetAllClick = onResetAllClick,
97+
)
98+
}
99+
100+
if (state.shouldShowEmptySearchItemsHint) {
101+
item {
102+
Text(
103+
text = stringResource(R.string.konfeature_plugin_search_empty),
104+
modifier = Modifier.padding(16.dp)
105+
)
99106
}
100107
}
101108

102-
state.items.forEach { item ->
109+
state.filteredItems.forEach { item ->
103110
if (item is KonfeatureItem.Config) {
104111
item(item.name) {
105112
ConfigItem(
106113
item = item,
107-
isCollapsed = item.name in state.collapsedConfigs,
114+
isCollapsed = !state.isSearchActive && item.name in state.collapsedConfigs,
108115
onHeaderClick = onHeaderClick
109116
)
110117
}
111118
}
112119

113-
if (item is KonfeatureItem.Value && item.configName !in state.collapsedConfigs) {
120+
val isExpanded = state.isSearchActive || item is KonfeatureItem.Value &&
121+
item.configName !in state.collapsedConfigs
122+
if (item is KonfeatureItem.Value && isExpanded) {
114123
item(item.key) { ValueItem(item = item, onEditClick) }
115124
item { Divider(modifier = Modifier.fillMaxWidth()) }
116125
}
117126
}
118127
}
119128
}
120129

130+
@Composable
131+
private fun KonfeatureHeader(
132+
searchQuery: String,
133+
onSearchQueryChange: (String) -> Unit,
134+
onRefreshClick: () -> Unit,
135+
onCollapseAllClick: () -> Unit,
136+
onResetAllClick: () -> Unit,
137+
modifier: Modifier = Modifier,
138+
) {
139+
Column(
140+
modifier = modifier
141+
.background(colorResource(id = CoreR.color.super_light_gray))
142+
.padding(horizontal = 16.dp)
143+
) {
144+
Spacer(modifier = Modifier.height(8.dp))
145+
KonfeatureSearchBar(
146+
query = searchQuery,
147+
onQueryChange = onSearchQueryChange,
148+
modifier = Modifier
149+
)
150+
Spacer(modifier = Modifier.height(8.dp))
151+
Row {
152+
Button(onClick = onRefreshClick) {
153+
Text(text = stringResource(id = R.string.konfeature_plugin_refresh))
154+
}
155+
Spacer(modifier = Modifier.weight(1f))
156+
Button(onClick = onCollapseAllClick) {
157+
Text(text = stringResource(id = R.string.konfeature_plugin_collapse_all))
158+
}
159+
Spacer(modifier = Modifier.weight(1f))
160+
Button(onClick = onResetAllClick) {
161+
Text(text = stringResource(id = R.string.konfeature_plugin_reset_all))
162+
}
163+
}
164+
}
165+
}
166+
167+
@Composable
168+
private fun KonfeatureSearchBar(
169+
query: String,
170+
onQueryChange: (String) -> Unit,
171+
modifier: Modifier = Modifier
172+
) {
173+
OutlinedTextField(
174+
value = query,
175+
onValueChange = onQueryChange,
176+
modifier = modifier.fillMaxWidth(),
177+
placeholder = {
178+
Text(text = stringResource(R.string.konfeature_plugin_search_hint))
179+
},
180+
leadingIcon = {
181+
Icon(
182+
imageVector = Icons.Default.Search,
183+
contentDescription = null
184+
)
185+
},
186+
trailingIcon = {
187+
if (query.isNotEmpty()) {
188+
IconButton(onClick = { onQueryChange("") }) {
189+
Icon(
190+
imageVector = Icons.Default.Clear,
191+
contentDescription = stringResource(R.string.konfeature_plugin_search_clear)
192+
)
193+
}
194+
}
195+
},
196+
singleLine = true,
197+
colors = TextFieldDefaults.outlinedTextFieldColors(
198+
backgroundColor = Color.White
199+
)
200+
)
201+
}
121202

122203
@Composable
123204
private fun ConfigItem(

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

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ internal class KonfeatureViewModel(
2525
private val konfeature: Konfeature,
2626
private val debugPanelInterceptor: KonfeatureDebugPanelInterceptor,
2727
) : PluginViewModel() {
28-
2928
private val _state = MutableStateFlow(KonfeatureViewState())
3029

3130
val state: Flow<KonfeatureViewState> = _state.asStateFlow()
@@ -85,13 +84,23 @@ internal class KonfeatureViewModel(
8584
_state.update { it.copy(editDialogState = EditDialogState(key, value, isDebugSource)) }
8685
}
8786

88-
fun onEditDialogCloseClik() {
87+
fun onEditDialogCloseClicked() {
8988
_state.update { it.copy(editDialogState = null) }
9089
}
9190

91+
fun onSearchQueryChanged(query: String) {
92+
_state.update { state ->
93+
val filteredItems = filterItems(state.items, query)
94+
state.copy(searchQuery = query, filteredItems = filteredItems)
95+
}
96+
}
97+
9298
private suspend fun updateItems() {
9399
val items = withContext(Dispatchers.IO) { getItems(konfeature) }
94-
_state.update { it.copy(items = items) }
100+
_state.update { state ->
101+
val filteredItems = filterItems(items, state.searchQuery)
102+
state.copy(items = items, filteredItems = filteredItems)
103+
}
95104
}
96105

97106
private fun getItems(konfeature: Konfeature): List<KonfeatureItem> {
@@ -152,6 +161,23 @@ internal class KonfeatureViewModel(
152161
else -> "Unknown"
153162
}
154163
}
164+
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)
178+
}
179+
}
180+
}
155181
}
156182

157183

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
package com.redmadrobot.debug.plugin.konfeature.ui.data
22

33
internal data class KonfeatureViewState(
4+
val searchQuery: String = "",
45
val collapsedConfigs: Set<String> = emptySet(),
56
val items: List<KonfeatureItem> = emptyList(),
7+
val filteredItems: List<KonfeatureItem> = emptyList(),
68
val editDialogState: EditDialogState? = null
7-
)
9+
) {
10+
val isSearchActive: Boolean
11+
get() = searchQuery.isNotBlank()
12+
val shouldShowEmptySearchItemsHint
13+
get() = isSearchActive && filteredItems.none { it is KonfeatureItem.Value }
14+
}

plugins/plugin-konfeature/src/main/res/values/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@
1818
<string name="konfeature_plugin_refresh">Refresh</string>
1919
<string name="konfeature_plugin_collapse_all">Collapse All</string>
2020
<string name="konfeature_plugin_reset_all">Reset All</string>
21+
22+
<string name="konfeature_plugin_search_hint">Search by key…</string>
23+
<string name="konfeature_plugin_search_clear">Clear search</string>
24+
<string name="konfeature_plugin_search_empty">No toggles found</string>
2125
</resources>

0 commit comments

Comments
 (0)