From 6735593517e7c4c801173d18f04c9b351a2adb56 Mon Sep 17 00:00:00 2001 From: Wynd Date: Sat, 28 Feb 2026 20:29:01 +0200 Subject: [PATCH] Tags list updates based on previously checked tags --- .../xyz/pixelatedw/recipe/data/RecipesView.kt | 42 ++++++++++++++++--- .../recipe/ui/components/MainScreen.kt | 32 ++++++++++---- 2 files changed, 60 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/xyz/pixelatedw/recipe/data/RecipesView.kt b/app/src/main/java/xyz/pixelatedw/recipe/data/RecipesView.kt index f4c1a84..d7a6ee6 100644 --- a/app/src/main/java/xyz/pixelatedw/recipe/data/RecipesView.kt +++ b/app/src/main/java/xyz/pixelatedw/recipe/data/RecipesView.kt @@ -6,13 +6,13 @@ import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update class RecipesView : ViewModel() { - private val _activeRecipe = MutableStateFlow( null ) + private val _activeRecipe = MutableStateFlow(null) val activeRecipe = _activeRecipe.asStateFlow() - private val _recipes = MutableStateFlow>( arrayListOf() ) + private val _recipes = MutableStateFlow>(arrayListOf()) val recipes = _recipes.asStateFlow() - private val _tagFilters = MutableStateFlow>( arrayListOf() ) + private val _tagFilters = MutableStateFlow>(arrayListOf()) val tagFilters = _tagFilters.asStateFlow() private val _search = MutableStateFlow(null) @@ -22,11 +22,42 @@ class RecipesView : ViewModel() { val keepScreenOn = _keepScreenOn.asStateFlow() fun reloadTagFilterState() { - _tagFilters.value = _tagFilters.value + // TODO Honestly quite a messy bit of logic, would be nice to have a more streamlined function here. + + // If this is empty that means no filters are active, which means we don't do any special tag counting + val activeFilterNames = _tagFilters.value.filter { f -> f.checked }.map { f -> f.tag } + val filtersMap = mutableMapOf() + + _recipes.value.stream() + .filter { it.tags.isNotEmpty() } + .filter { + if (activeFilterNames.isNotEmpty()) { + it.tags.count { t -> activeFilterNames.contains(t.name) } >= activeFilterNames.size + } else { + true + } + } + .forEach { + it.tags.forEach { tag -> + val count = filtersMap[tag.name] ?: 0 + filtersMap[tag.name] = count + 1; + } + } + + if (activeFilterNames.isNotEmpty()) { + _tagFilters.value = _tagFilters.value.map { + it.count = filtersMap[it.tag] ?: 0 + it + }.toList() + } else { + val filters = filtersMap.map { TagFilter(it.key, count = it.value) }.toList() + _tagFilters.update { filters.distinct().sortedBy { it.tag } } + } } fun setTagFilterState(tag: String, state: Boolean) { - _tagFilters.value = _tagFilters.value.toMutableList().apply { replaceAll { it -> if (tag == it.tag) it.checked = state; it } }.toList() + _tagFilters.value = _tagFilters.value.toMutableList() + .apply { replaceAll { it -> if (tag == it.tag) it.checked = state; it } }.toList() } fun setKeepScreenOn(flag: Boolean) { @@ -48,7 +79,6 @@ class RecipesView : ViewModel() { } val filters = filtersMap.map { it -> TagFilter(it.key, count = it.value) }.toList() - _tagFilters.update { filters.distinct().sortedBy { it.tag } } } diff --git a/app/src/main/java/xyz/pixelatedw/recipe/ui/components/MainScreen.kt b/app/src/main/java/xyz/pixelatedw/recipe/ui/components/MainScreen.kt index 46be3fa..f3e5c85 100644 --- a/app/src/main/java/xyz/pixelatedw/recipe/ui/components/MainScreen.kt +++ b/app/src/main/java/xyz/pixelatedw/recipe/ui/components/MainScreen.kt @@ -11,9 +11,12 @@ import androidx.compose.foundation.lazy.items import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Settings +import androidx.compose.material3.Badge +import androidx.compose.material3.BadgedBox import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue @@ -43,6 +46,8 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) { val search = view.search.collectAsState() val filters = view.tagFilters.collectAsState() + var activeTags = 0 + val navController = rememberNavController() var openDeletionDialog by remember { mutableStateOf(false) } @@ -93,15 +98,15 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) { Column(modifier = Modifier.padding(padding)) { Row( modifier = Modifier - .fillMaxWidth() - .padding(8.dp), + .fillMaxWidth() + .padding(8.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp) ) { OutlinedTextField( modifier = Modifier - .weight(0.7f) - .padding(end = 4.dp), + .weight(0.7f) + .padding(end = 4.dp), value = search.value.orEmpty(), onValueChange = { search -> view.setSearch(search) }, ) @@ -124,10 +129,20 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) { modifier = Modifier.weight(0.1f), onClick = { openTagFilterDialog = true }, ) { - Icon( - imageVector = ImageVector.vectorResource(R.drawable.filter_24), - contentDescription = "Toggle tags filtering menu" - ) + BadgedBox( + badge = { + if (activeTags > 0) { + Badge { + Text("$activeTags") + } + } + } + ) { + Icon( + imageVector = ImageVector.vectorResource(R.drawable.filter_24), + contentDescription = "Toggle tags filtering menu" + ) + } } } // Options @@ -195,6 +210,7 @@ fun MainScreen(ctx: MainActivity, padding: PaddingValues, view: RecipesView) { onAccept = { openTagFilterDialog = false view.reloadTagFilterState() + activeTags = filters.value.count { f -> f.checked } }, view, )