From 21d791953335b2bc289747ff9444b1a77acdac6e Mon Sep 17 00:00:00 2001 From: Aitor Viana Date: Tue, 5 Nov 2024 12:01:27 +0000 Subject: [PATCH] Improve search logic in feature inventory screen (#5232) Task/Issue URL: https://app.asana.com/0/1198194956794324/1208633884628758/f ### Description Improve the (internal) feature flag inventory screen search logic: Before: * searches would filter feature flags that match the search text After: * searches filter feature flags that match the search text, but: * if match is a top-level features, all sub-features are also considered a match (even if their name doesn't match) * if match is a sub-feature, parent feature is also considered a match (even if its name doesn't match) ### Steps to test this PR _Test_ - [x] install from this branch, open app -> settings -> feature flag inventory - [x] search for a sub-feature by name - [x] verify sub-feature and its parent top-level feature are displayed - [x] search for a top-level feature by name (eg. autofill) - [x] verify the top-level feature and ALL its sub-features are displayed - [x] smoke test enable/disable features --- .../feature/toggles/api/FeatureToggles.kt | 11 +++++ .../ui/FeatureToggleInventoryActivity.kt | 40 ++++++++++++++----- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureToggles.kt b/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureToggles.kt index 075fce98fc58..154419ad14c3 100644 --- a/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureToggles.kt +++ b/feature-toggles/feature-toggles-api/src/main/java/com/duckduckgo/feature/toggles/api/FeatureToggles.kt @@ -306,6 +306,17 @@ internal class ToggleImpl constructor( private val callback: FeatureTogglesCallback?, ) : Toggle { + override fun equals(other: Any?): Boolean { + if (other !is Toggle) { + return false + } + return this.featureName() == other.featureName() + } + + override fun hashCode(): Int { + return this.featureName().hashCode() + } + private fun Toggle.State.evaluateTargetMatching(isExperiment: Boolean): Boolean { val variant = appVariantProvider.invoke() // no targets then consider always treated diff --git a/feature-toggles/feature-toggles-internal/src/main/java/com/duckduckgo/examplefeature/internal/ui/FeatureToggleInventoryActivity.kt b/feature-toggles/feature-toggles-internal/src/main/java/com/duckduckgo/examplefeature/internal/ui/FeatureToggleInventoryActivity.kt index 4d1439d5d1ff..8436dbaac8f8 100644 --- a/feature-toggles/feature-toggles-internal/src/main/java/com/duckduckgo/examplefeature/internal/ui/FeatureToggleInventoryActivity.kt +++ b/feature-toggles/feature-toggles-internal/src/main/java/com/duckduckgo/examplefeature/internal/ui/FeatureToggleInventoryActivity.kt @@ -117,25 +117,43 @@ class FeatureToggleInventoryActivity : DuckDuckGoActivity() { private suspend fun getFeatureViews(): List = withContext(dispatcherProvider.io()) { val toggles = this@FeatureToggleInventoryActivity.toggles.await() val match = featureNameFilter.get().lowercase() - val parentFeature = toggles + val parentFeatures = toggles .filter { it.featureName().parentName == null } .sortedBy { it.featureName().name.lowercase() } val subFeatures = toggles .filter { it.featureName().parentName != null } .sortedBy { it.featureName().name.lowercase() } + val features = mutableListOf().apply { - for (parent in parentFeature) { - add(parent) - addAll(subFeatures.filter { it.featureName().parentName == parent.featureName().name }) + // add parent features that match and all their sub-features + parentFeatures.forEach { parentFeature -> + if (match.isNotBlank()) { + if (parentFeature.featureName().name.lowercase().contains(match)) { + add(parentFeature) + // add also all sub-features + addAll( + subFeatures.filter { it.featureName().parentName == parentFeature.featureName().name }, + ) + } + } else { + for (parent in parentFeatures) { + add(parent) + addAll(subFeatures.filter { it.featureName().parentName == parent.featureName().name }) + } + } } - }.filter { - if (match.isNotBlank()) { - // Apply search box filter if needed - it.featureName().name.lowercase().contains(match) - } else { - true + + // add sub-features that match and their parent feature + subFeatures.forEach { feature -> + if (match.isNotBlank() && feature.featureName().name.lowercase().contains(match)) { + // add its parent too + addAll( + parentFeatures.filter { it.featureName().name == feature.featureName().parentName }, + ) + add(feature) + } } - } + }.distinct() // de-dup val views = features.map { feature -> OneLineListItem(this@FeatureToggleInventoryActivity).apply {