feat(debug): add a toggle to AND/OR all filters. (#2265)

This commit is contained in:
DaneEvans
2025-06-27 23:13:21 +10:00
committed by GitHub
parent ea25a8198a
commit dd50cf230e
6 changed files with 66 additions and 15 deletions

View File

@@ -31,6 +31,7 @@ import org.junit.runner.RunWith
import androidx.test.platform.app.InstrumentationRegistry
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import com.geeksville.mesh.ui.debug.FilterMode
@RunWith(AndroidJUnit4::class)
class DebugFiltersTest {
@@ -67,13 +68,15 @@ class DebugFiltersTest {
var customFilterText by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf("") }
com.geeksville.mesh.ui.debug.DebugActiveFilters(
filterTexts = filterTexts,
onFilterTextsChange = { filterTexts = it }
onFilterTextsChange = { filterTexts = it },
filterMode = FilterMode.OR,
onFilterModeChange = {}
)
com.geeksville.mesh.ui.debug.DebugCustomFilterInput(
customFilterText = customFilterText,
onCustomFilterTextChange = { customFilterText = it },
filterTexts = filterTexts,
onFilterTextsChange = { filterTexts = it }
onFilterTextsChange = { filterTexts = it },
)
}
// Add a custom filter
@@ -92,7 +95,9 @@ class DebugFiltersTest {
var filterTexts by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(listOf("A", "B")) }
com.geeksville.mesh.ui.debug.DebugActiveFilters(
filterTexts = filterTexts,
onFilterTextsChange = { filterTexts = it }
onFilterTextsChange = { filterTexts = it },
filterMode = FilterMode.OR,
onFilterModeChange = {}
)
}
// The active filters label and chips should be visible

View File

@@ -37,6 +37,8 @@ import org.junit.runner.RunWith
import androidx.test.platform.app.InstrumentationRegistry
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
import com.geeksville.mesh.ui.debug.FilterMode
import com.geeksville.mesh.ui.debug.DebugActiveFilters
@RunWith(AndroidJUnit4::class)
class DebugSearchTest {
@@ -137,7 +139,9 @@ class DebugSearchTest {
var customFilterText by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf("") }
com.geeksville.mesh.ui.debug.DebugActiveFilters(
filterTexts = filterTexts,
onFilterTextsChange = { filterTexts = it }
onFilterTextsChange = { filterTexts = it },
filterMode = FilterMode.OR,
onFilterModeChange = {}
)
com.geeksville.mesh.ui.debug.DebugCustomFilterInput(
customFilterText = customFilterText,
@@ -162,7 +166,9 @@ class DebugSearchTest {
var filterTexts by androidx.compose.runtime.remember { androidx.compose.runtime.mutableStateOf(listOf("A", "B")) }
com.geeksville.mesh.ui.debug.DebugActiveFilters(
filterTexts = filterTexts,
onFilterTextsChange = { filterTexts = it }
onFilterTextsChange = { filterTexts = it },
filterMode = FilterMode.OR,
onFilterModeChange = {}
)
}
// The active filters label and chips should be visible

View File

@@ -108,12 +108,24 @@ internal fun DebugScreen(
val filterTexts by viewModel.filterTexts.collectAsStateWithLifecycle()
val selectedLogId by viewModel.selectedLogId.collectAsStateWithLifecycle()
val filteredLogs = remember(logs, filterTexts) {
var filterMode by remember { mutableStateOf(FilterMode.OR) }
val filteredLogs = remember(logs, filterTexts, filterMode) {
logs.filter { log ->
filterTexts.isEmpty() || filterTexts.any { filterText ->
log.logMessage.contains(filterText, ignoreCase = true) ||
log.messageType.contains(filterText, ignoreCase = true) ||
log.formattedReceivedDate.contains(filterText, ignoreCase = true)
if (filterTexts.isEmpty()) {
true
} else { when (filterMode) {
FilterMode.OR -> filterTexts.any { filterText ->
log.logMessage.contains(filterText, ignoreCase = true) ||
log.messageType.contains(filterText, ignoreCase = true) ||
log.formattedReceivedDate.contains(filterText, ignoreCase = true)
}
FilterMode.AND -> filterTexts.all { filterText ->
log.logMessage.contains(filterText, ignoreCase = true) ||
log.messageType.contains(filterText, ignoreCase = true) ||
log.formattedReceivedDate.contains(filterText, ignoreCase = true)
}
}
}
}.toImmutableList()
}
@@ -136,7 +148,6 @@ internal fun DebugScreen(
listState.requestScrollToItem(searchState.allMatches[searchState.currentMatchIndex].logIndex)
}
}
Column(
modifier = Modifier.fillMaxSize()
) {
@@ -156,9 +167,10 @@ internal fun DebugScreen(
searchState = searchState,
filterTexts = filterTexts,
presetFilters = viewModel.presetFilters,
filterMode = filterMode,
onFilterModeChange = { filterMode = it }
)
}
items(filteredLogs, key = { it.uuid }) { log ->
DebugItem(
modifier = Modifier.animateItem(),
@@ -708,7 +720,6 @@ fun DebugMenuActions(
contentDescription = "Clear All"
)
}
if (showDeleteLogsDialog) {
SimpleAlertDialog(
title = R.string.debug_clear,

View File

@@ -215,10 +215,13 @@ internal fun DebugFilterBar(
}
}
@Suppress("LongMethod")
@Composable
internal fun DebugActiveFilters(
filterTexts: List<String>,
onFilterTextsChange: (List<String>) -> Unit,
filterMode: FilterMode,
onFilterModeChange: (FilterMode) -> Unit,
modifier: Modifier = Modifier
) {
val colorScheme = MaterialTheme.colorScheme
@@ -237,6 +240,20 @@ internal fun DebugActiveFilters(
text = stringResource(R.string.debug_active_filters),
style = TextStyle(fontWeight = FontWeight.Bold)
)
TextButton(
onClick = {
onFilterModeChange(
if (filterMode == FilterMode.OR) FilterMode.AND else FilterMode.OR
)
}
) {
Text(if (filterMode == FilterMode.OR) {
stringResource(R.string.match_any)
} else {
stringResource(R.string.match_all)
}
)
}
IconButton(
onClick = { onFilterTextsChange(emptyList()) }
) {
@@ -278,3 +295,5 @@ internal fun DebugActiveFilters(
}
}
}
enum class FilterMode { OR, AND }

View File

@@ -163,6 +163,8 @@ internal fun DebugSearchState(
onPreviousMatch: () -> Unit,
onClearSearch: () -> Unit,
onFilterTextsChange: (List<String>) -> Unit,
filterMode: FilterMode,
onFilterModeChange: (FilterMode) -> Unit,
) {
val colorScheme = MaterialTheme.colorScheme
var customFilterText by remember { mutableStateOf("") }
@@ -198,7 +200,9 @@ internal fun DebugSearchState(
}
DebugActiveFilters(
filterTexts = filterTexts,
onFilterTextsChange = onFilterTextsChange
onFilterTextsChange = onFilterTextsChange,
filterMode = filterMode,
onFilterModeChange = onFilterModeChange
)
}
}
@@ -209,6 +213,8 @@ fun DebugSearchStateviewModelDefaults(
searchState: SearchState,
filterTexts: List<String>,
presetFilters: List<String>,
filterMode: FilterMode,
onFilterModeChange: (FilterMode) -> Unit,
) {
val viewModel: DebugViewModel = hiltViewModel()
DebugSearchState(
@@ -221,6 +227,8 @@ fun DebugSearchStateviewModelDefaults(
onPreviousMatch = viewModel.searchManager::goToPreviousMatch,
onClearSearch = viewModel.searchManager::clearSearch,
onFilterTextsChange = viewModel.filterManager::setFilterTexts,
filterMode = filterMode,
onFilterModeChange = onFilterModeChange
)
}

View File

@@ -165,9 +165,11 @@
<string name="debug_logs_export">Export Logs</string>
<string name="debug_last_messages">500 last messages</string>
<string name="debug_filters">Filters</string>
<string name="debug_active_filters">Active filters (Match any)</string>
<string name="debug_active_filters">Active filters</string>
<string name="debug_default_search">Search in logs…</string>
<string name="debug_clear">Clear Logs</string>
<string name="match_any">Match Any | All</string>
<string name="match_all">Match All | Any</string>
<string name="debug_clear_logs_confirm">This will remove all log packets and database entries from your device - It is a full reset, and is permanent.</string>
<string name="clear">Clear</string>
<string name="updating_firmware">Updating firmware, wait up to eight minutes…</string>