Compare commits
2 Commits
79551ab69d
...
f83bf62655
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f83bf62655 | ||
|
|
6c067f64ce |
@@ -2,7 +2,6 @@ package cc.n0th1ng.tripmoney.data.dao
|
|||||||
|
|
||||||
import androidx.room.Dao
|
import androidx.room.Dao
|
||||||
import androidx.room.Delete
|
import androidx.room.Delete
|
||||||
import androidx.room.Insert
|
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import androidx.room.Transaction
|
import androidx.room.Transaction
|
||||||
import androidx.room.Upsert
|
import androidx.room.Upsert
|
||||||
@@ -21,7 +20,7 @@ interface CategoryDao {
|
|||||||
@Transaction
|
@Transaction
|
||||||
@Query(
|
@Query(
|
||||||
"""
|
"""
|
||||||
SELECT * FROM category WHERE archived is 0
|
SELECT * FROM category WHERE archived is 0 ORDER BY name
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
fun categories(): Flow<List<Category>>
|
fun categories(): Flow<List<Category>>
|
||||||
|
|||||||
@@ -98,7 +98,8 @@ fun AddExpenseBottomSheet(
|
|||||||
expenseDtoToEdit = expenseDtoToEdit,
|
expenseDtoToEdit = expenseDtoToEdit,
|
||||||
state = state,
|
state = state,
|
||||||
currentTrip = currentTrip ?: Trip.DUMMY,
|
currentTrip = currentTrip ?: Trip.DUMMY,
|
||||||
categories = categories
|
categories = categories,
|
||||||
|
onSaveCategory = {expenseAndCategoryViewModel.save(it)}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -111,7 +112,8 @@ fun AddExpenseBottomSheet(
|
|||||||
expenseDtoToEdit: ExpenseDto?,
|
expenseDtoToEdit: ExpenseDto?,
|
||||||
state: SheetState,
|
state: SheetState,
|
||||||
currentTrip: Trip,
|
currentTrip: Trip,
|
||||||
categories: List<Category>
|
categories: List<Category>,
|
||||||
|
onSaveCategory: (Category) -> Unit
|
||||||
) {
|
) {
|
||||||
val currentTripId = currentTrip.id
|
val currentTripId = currentTrip.id
|
||||||
|
|
||||||
@@ -319,7 +321,8 @@ fun AddExpenseBottomSheet(
|
|||||||
category = selectedCategory
|
category = selectedCategory
|
||||||
},
|
},
|
||||||
selected = category,
|
selected = category,
|
||||||
categories = categories
|
categories = categories,
|
||||||
|
onSaveCategory = onSaveCategory
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -578,7 +581,8 @@ fun PreviewAddExpenseDisabled() {
|
|||||||
LocalDate.parse("2020-01-15"),
|
LocalDate.parse("2020-01-15"),
|
||||||
Currencies.entries.random().name
|
Currencies.entries.random().name
|
||||||
),
|
),
|
||||||
categories = categoriesToPreview
|
categories = categoriesToPreview,
|
||||||
|
{}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -622,7 +626,8 @@ fun PreviewAddExpenseEnabled() {
|
|||||||
LocalDate.parse("2020-01-11"),
|
LocalDate.parse("2020-01-11"),
|
||||||
Currencies.entries.random().name
|
Currencies.entries.random().name
|
||||||
),
|
),
|
||||||
categories = categoriesToPreview
|
categories = categoriesToPreview,
|
||||||
|
{}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,22 +13,26 @@ import androidx.compose.material3.Icon
|
|||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.core.graphics.toColorInt
|
import androidx.core.graphics.toColorInt
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|
||||||
import cc.n0th1ng.tripmoney.R.string
|
import cc.n0th1ng.tripmoney.R.string
|
||||||
import cc.n0th1ng.tripmoney.data.entity.Category
|
import cc.n0th1ng.tripmoney.data.entity.Category
|
||||||
import cc.n0th1ng.tripmoney.screens.AddCategoryDialog
|
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
|
import com.composables.icons.materialsymbols.outlined.R
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -37,22 +41,41 @@ fun CategorySelectionDialog(
|
|||||||
onCategorySelected: (Category) -> Unit,
|
onCategorySelected: (Category) -> Unit,
|
||||||
selected: Category?,
|
selected: Category?,
|
||||||
categories: List<Category>,
|
categories: List<Category>,
|
||||||
|
onSaveCategory: (Category) -> Unit
|
||||||
) {
|
) {
|
||||||
val expenseAndCategoryViewModel: ExpenseAndCategoryViewModel = hiltViewModel()
|
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
var showAddCategoryDialog by remember { mutableStateOf(false) }
|
var showAddCategoryDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
AlertDialog(
|
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 {
|
Column {
|
||||||
|
var search by remember { mutableStateOf("") }
|
||||||
|
val filteredCategories = if (search.isBlank()) {
|
||||||
|
categories
|
||||||
|
} else {
|
||||||
|
categories.filter { category ->
|
||||||
|
category.name.lowercase().contains(search.lowercase())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
modifier = Modifier.heightIn(max = 300.dp),
|
modifier = Modifier.heightIn(max = 300.dp),
|
||||||
state = listState,
|
state = listState,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
items(
|
items(
|
||||||
count = categories.size,
|
count = filteredCategories.size,
|
||||||
key = { index -> categories[index].id }) { index ->
|
key = { index -> filteredCategories[index].id }) { index ->
|
||||||
val category = categories[index]
|
val category = filteredCategories[index]
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -83,24 +106,41 @@ fun CategorySelectionDialog(
|
|||||||
.clickable {
|
.clickable {
|
||||||
showAddCategoryDialog = true
|
showAddCategoryDialog = true
|
||||||
}
|
}
|
||||||
.padding(top = 15.dp),
|
.padding(bottom = 10.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically) {
|
verticalAlignment = Alignment.CenterVertically) {
|
||||||
Icon(
|
Icon(
|
||||||
painter = painterResource(R.drawable.materialsymbols_ic_add_outlined),
|
painter = painterResource(R.drawable.materialsymbols_ic_add_outlined),
|
||||||
contentDescription = stringResource(string.category)
|
contentDescription = stringResource(string.category)
|
||||||
)
|
)
|
||||||
Text(
|
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) {
|
if (showAddCategoryDialog) {
|
||||||
AddCategoryDialog(onDismiss = {
|
AddCategoryDialog(onDismiss = {
|
||||||
showAddCategoryDialog = false
|
showAddCategoryDialog = false
|
||||||
}, onSave = { category ->
|
}, onSave = { category ->
|
||||||
expenseAndCategoryViewModel.save(category)
|
onSaveCategory(category)
|
||||||
showAddCategoryDialog = false
|
showAddCategoryDialog = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@AllPreviews
|
||||||
|
@Composable
|
||||||
|
fun PreviewCategorySelectionDialog() {
|
||||||
|
TripMoneyTheme {
|
||||||
|
CategorySelectionDialog(
|
||||||
|
{}, {},
|
||||||
|
categoriesToPreview.random(), categoriesToPreview, {})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -12,9 +12,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
|
|||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ButtonDefaults
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.OutlinedTextField
|
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -25,14 +23,13 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.painterResource
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import cc.n0th1ng.tripmoney.R
|
import cc.n0th1ng.tripmoney.R
|
||||||
import cc.n0th1ng.tripmoney.theme.TripMoneyTheme
|
import cc.n0th1ng.tripmoney.theme.TripMoneyTheme
|
||||||
import cc.n0th1ng.tripmoney.utils.AllPreviews
|
import cc.n0th1ng.tripmoney.utils.AllPreviews
|
||||||
import cc.n0th1ng.tripmoney.utils.Currencies
|
import cc.n0th1ng.tripmoney.utils.Currencies
|
||||||
import com.composables.icons.materialsymbols.outlined.R.drawable
|
import cc.n0th1ng.tripmoney.utils.SearchTextOutlined
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CurrencySelectionDialog(
|
fun CurrencySelectionDialog(
|
||||||
@@ -57,20 +54,6 @@ fun CurrencySelectionDialog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Column(verticalArrangement = Arrangement.spacedBy(5.dp)) {
|
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()) {
|
val filteredCurrencies = if (search.isBlank()) {
|
||||||
currencies
|
currencies
|
||||||
} else {
|
} else {
|
||||||
@@ -79,7 +62,12 @@ fun CurrencySelectionDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LazyColumn(state = scrollState) {
|
LazyColumn(
|
||||||
|
state = scrollState,
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.padding(bottom = 10.dp)
|
||||||
|
) {
|
||||||
items(
|
items(
|
||||||
count = filteredCurrencies.size,
|
count = filteredCurrencies.size,
|
||||||
key = { index -> filteredCurrencies[index] }
|
key = { index -> filteredCurrencies[index] }
|
||||||
@@ -104,6 +92,8 @@ fun CurrencySelectionDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
SearchTextOutlined(
|
||||||
|
text = search, onTextChange = { newText -> search = newText })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
confirmButton = {},
|
confirmButton = {},
|
||||||
|
|||||||
@@ -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"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user