Compare commits

..

2 Commits

Author SHA1 Message Date
Rafal Wisniewski
f83bf62655 fix: add search to category picker 2026-05-06 10:49:23 +02:00
Rafal Wisniewski
6c067f64ce fix: add search to category picker 2026-05-06 10:49:09 +02:00
5 changed files with 116 additions and 37 deletions

View File

@@ -2,7 +2,6 @@ package cc.n0th1ng.tripmoney.data.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Transaction
import androidx.room.Upsert
@@ -21,7 +20,7 @@ interface CategoryDao {
@Transaction
@Query(
"""
SELECT * FROM category WHERE archived is 0
SELECT * FROM category WHERE archived is 0 ORDER BY name
"""
)
fun categories(): Flow<List<Category>>

View File

@@ -98,7 +98,8 @@ fun AddExpenseBottomSheet(
expenseDtoToEdit = expenseDtoToEdit,
state = state,
currentTrip = currentTrip ?: Trip.DUMMY,
categories = categories
categories = categories,
onSaveCategory = {expenseAndCategoryViewModel.save(it)}
)
}
@@ -111,7 +112,8 @@ fun AddExpenseBottomSheet(
expenseDtoToEdit: ExpenseDto?,
state: SheetState,
currentTrip: Trip,
categories: List<Category>
categories: List<Category>,
onSaveCategory: (Category) -> Unit
) {
val currentTripId = currentTrip.id
@@ -319,7 +321,8 @@ fun AddExpenseBottomSheet(
category = selectedCategory
},
selected = category,
categories = categories
categories = categories,
onSaveCategory = onSaveCategory
)
}
}
@@ -578,7 +581,8 @@ fun PreviewAddExpenseDisabled() {
LocalDate.parse("2020-01-15"),
Currencies.entries.random().name
),
categories = categoriesToPreview
categories = categoriesToPreview,
{}
)
}
@@ -622,7 +626,8 @@ fun PreviewAddExpenseEnabled() {
LocalDate.parse("2020-01-11"),
Currencies.entries.random().name
),
categories = categoriesToPreview
categories = categoriesToPreview,
{}
)
}

View File

@@ -13,22 +13,26 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.graphics.toColorInt
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import cc.n0th1ng.tripmoney.R.string
import cc.n0th1ng.tripmoney.data.entity.Category
import cc.n0th1ng.tripmoney.screens.AddCategoryDialog
import cc.n0th1ng.tripmoney.viewmodel.ExpenseAndCategoryViewModel
import cc.n0th1ng.tripmoney.screens.addexpense.categoriesToPreview
import cc.n0th1ng.tripmoney.theme.TripMoneyTheme
import cc.n0th1ng.tripmoney.utils.AllPreviews
import cc.n0th1ng.tripmoney.utils.SearchTextOutlined
import com.composables.icons.materialsymbols.outlined.R
@Composable
@@ -37,22 +41,41 @@ fun CategorySelectionDialog(
onCategorySelected: (Category) -> Unit,
selected: Category?,
categories: List<Category>,
onSaveCategory: (Category) -> Unit
) {
val expenseAndCategoryViewModel: ExpenseAndCategoryViewModel = hiltViewModel()
val listState = rememberLazyListState()
var showAddCategoryDialog by remember { mutableStateOf(false) }
AlertDialog(
onDismissRequest = onDismiss, title = { Text(stringResource(string.pick_category)) }, text = {
onDismissRequest = onDismiss,
title = { Text(stringResource(string.pick_category)) },
text = {
val focusRequester = remember { FocusRequester() }
LaunchedEffect(Unit) {
if(selected != null) {
listState.animateScrollToItem(categories.indexOfFirst { it == selected })
}
// focusRequester.requestFocus()
}
Column {
var search by remember { mutableStateOf("") }
val filteredCategories = if (search.isBlank()) {
categories
} else {
categories.filter { category ->
category.name.lowercase().contains(search.lowercase())
}
}
LazyColumn(
modifier = Modifier.heightIn(max = 300.dp),
state = listState,
horizontalAlignment = Alignment.CenterHorizontally,
) {
items(
count = categories.size,
key = { index -> categories[index].id }) { index ->
val category = categories[index]
count = filteredCategories.size,
key = { index -> filteredCategories[index].id }) { index ->
val category = filteredCategories[index]
Row(
modifier = Modifier
.fillMaxWidth()
@@ -83,24 +106,41 @@ fun CategorySelectionDialog(
.clickable {
showAddCategoryDialog = true
}
.padding(top = 15.dp),
.padding(bottom = 10.dp),
verticalAlignment = Alignment.CenterVertically) {
Icon(
painter = painterResource(R.drawable.materialsymbols_ic_add_outlined),
contentDescription = stringResource(string.category)
)
Text(
text = stringResource(string.add_new), modifier = Modifier.padding(start = 8.dp),
text = stringResource(string.add_new),
modifier = Modifier.padding(start = 8.dp),
)
}
SearchTextOutlined(
text = search,
onTextChange = { newText -> search = newText },
focusRequester = focusRequester
)
}
}, confirmButton = {})
},
confirmButton = {})
if (showAddCategoryDialog) {
AddCategoryDialog(onDismiss = {
showAddCategoryDialog = false
}, onSave = { category ->
expenseAndCategoryViewModel.save(category)
onSaveCategory(category)
showAddCategoryDialog = false
})
}
}
@AllPreviews
@Composable
fun PreviewCategorySelectionDialog() {
TripMoneyTheme {
CategorySelectionDialog(
{}, {},
categoriesToPreview.random(), categoriesToPreview, {})
}
}

View File

@@ -12,9 +12,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -25,14 +23,13 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import cc.n0th1ng.tripmoney.R
import cc.n0th1ng.tripmoney.theme.TripMoneyTheme
import cc.n0th1ng.tripmoney.utils.AllPreviews
import cc.n0th1ng.tripmoney.utils.Currencies
import com.composables.icons.materialsymbols.outlined.R.drawable
import cc.n0th1ng.tripmoney.utils.SearchTextOutlined
@Composable
fun CurrencySelectionDialog(
@@ -57,20 +54,6 @@ fun CurrencySelectionDialog(
}
Column(verticalArrangement = Arrangement.spacedBy(5.dp)) {
OutlinedTextField(
value = search,
onValueChange = { newText ->
search = newText
},
modifier = Modifier.fillMaxWidth(),
trailingIcon = {
Icon(
painter = painterResource(drawable.materialsymbols_ic_search_outlined),
contentDescription = "search"
)
}
)
val filteredCurrencies = if (search.isBlank()) {
currencies
} else {
@@ -79,7 +62,12 @@ fun CurrencySelectionDialog(
}
}
LazyColumn(state = scrollState) {
LazyColumn(
state = scrollState,
modifier = Modifier
.weight(1f)
.padding(bottom = 10.dp)
) {
items(
count = filteredCurrencies.size,
key = { index -> filteredCurrencies[index] }
@@ -104,6 +92,8 @@ fun CurrencySelectionDialog(
}
}
}
SearchTextOutlined(
text = search, onTextChange = { newText -> search = newText })
}
},
confirmButton = {},

View File

@@ -0,0 +1,45 @@
package cc.n0th1ng.tripmoney.utils
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material3.Icon
import androidx.compose.material3.OutlinedTextField
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.painterResource
import com.composables.icons.materialsymbols.outlined.R.drawable
@Composable
fun SearchTextOutlined(
modifier: Modifier = Modifier,
text: String,
onTextChange: (String) -> Unit,
focusRequester: FocusRequester = FocusRequester()
) {
OutlinedTextField(
value = text,
onValueChange = onTextChange,
modifier = modifier
.fillMaxWidth()
.focusRequester(focusRequester),
trailingIcon = {
if (text.isNotBlank()) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = "close",
modifier = Modifier.clickable(true, onClick = { onTextChange("") })
)
} else {
Icon(
painter = painterResource(drawable.materialsymbols_ic_search_outlined),
contentDescription = "search"
)
}
}
)
}