Skip to content

Commit e06cca3

Browse files
committed
🏁 Added Category input dialog.
Signed-off-by: Prasanna Anbazhagan <panbazhagan@twilio.com>
1 parent 8000c4f commit e06cca3

5 files changed

Lines changed: 128 additions & 37 deletions

File tree

app/src/main/java/app/expense/tracker/ui/utils/AmountInputDialog.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ fun AmountInputDialog(
5555
Row(modifier = Modifier.padding(dimensionResource(id = R.dimen.default_padding))) {
5656
Spacer(modifier = Modifier.weight(1f))
5757
TextButton(onClick = { onDismiss() }) {
58-
Text(text = "Cancel")
58+
Text(text = stringResource(R.string.cancel))
5959
}
6060
TextButton(
6161
enabled = amountState.value.toDoubleOrNull() != null,
@@ -65,7 +65,7 @@ fun AmountInputDialog(
6565
)
6666
}
6767
) {
68-
Text(text = "Confirm")
68+
Text(text = stringResource(id = R.string.confirm))
6969
}
7070
}
7171
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
package app.expense.tracker.ui.utils
2+
3+
import androidx.compose.foundation.layout.Box
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.Row
6+
import androidx.compose.foundation.layout.Spacer
7+
import androidx.compose.foundation.layout.fillMaxWidth
8+
import androidx.compose.foundation.layout.padding
9+
import androidx.compose.material3.Card
10+
import androidx.compose.material3.ExperimentalMaterial3Api
11+
import androidx.compose.material3.Text
12+
import androidx.compose.material3.TextButton
13+
import androidx.compose.runtime.Composable
14+
import androidx.compose.runtime.collectAsState
15+
import androidx.compose.runtime.mutableStateOf
16+
import androidx.compose.runtime.rememberCoroutineScope
17+
import androidx.compose.runtime.saveable.rememberSaveable
18+
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.res.dimensionResource
20+
import androidx.compose.ui.res.stringResource
21+
import androidx.compose.ui.unit.dp
22+
import androidx.compose.ui.window.Dialog
23+
import androidx.hilt.navigation.compose.hiltViewModel
24+
import app.expense.presentation.viewModels.CategorySuggestionViewModel
25+
import app.expense.tracker.R
26+
import kotlinx.coroutines.launch
27+
28+
@OptIn(ExperimentalMaterial3Api::class)
29+
@Composable
30+
fun CategoryInputDialog(
31+
onCategoryEntered: (String) -> Unit,
32+
onDismiss: () -> Unit,
33+
viewModel: CategorySuggestionViewModel = hiltViewModel()
34+
) {
35+
val coroutineScope = rememberCoroutineScope()
36+
val suggestions = viewModel.categoriesState.collectAsState().value
37+
val category = rememberSaveable { mutableStateOf("") }
38+
Dialog(onDismissRequest = {
39+
onDismiss()
40+
}) {
41+
Card(modifier = Modifier.padding(dimensionResource(id = R.dimen.default_padding))) {
42+
Box(modifier = Modifier.padding(dimensionResource(id = R.dimen.default_padding))) {
43+
Column {
44+
AutoCompleteTextField(
45+
value = category.value,
46+
label = stringResource(id = R.string.category),
47+
suggestions = suggestions,
48+
onCategoryEntered = { value ->
49+
category.value = value
50+
coroutineScope.launch {
51+
viewModel.getCategories(value)
52+
}
53+
},
54+
onCategorySelect = { value ->
55+
category.value = value
56+
},
57+
modifier = Modifier
58+
.fillMaxWidth()
59+
.padding(16.dp)
60+
)
61+
Spacer(modifier = Modifier.padding(dimensionResource(id = R.dimen.small_gap)))
62+
Row(modifier = Modifier.padding(dimensionResource(id = R.dimen.default_padding))) {
63+
Spacer(modifier = Modifier.weight(1f))
64+
TextButton(onClick = { onDismiss() }) {
65+
Text(text = stringResource(R.string.cancel))
66+
}
67+
TextButton(
68+
enabled = category.value.isNotEmpty(),
69+
onClick = {
70+
onCategoryEntered(category.value)
71+
}
72+
) {
73+
Text(text = stringResource(R.string.confirm))
74+
}
75+
}
76+
}
77+
}
78+
}
79+
}
80+
}

app/src/main/java/app/expense/tracker/ui/views/addExpense/CategoryView.kt

Lines changed: 41 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,63 +3,70 @@ package app.expense.tracker.ui.views.addExpense
33
import androidx.compose.foundation.horizontalScroll
44
import androidx.compose.foundation.layout.Column
55
import androidx.compose.foundation.layout.Row
6-
import androidx.compose.foundation.layout.fillMaxWidth
6+
import androidx.compose.foundation.layout.Spacer
77
import androidx.compose.foundation.layout.padding
88
import androidx.compose.foundation.rememberScrollState
99
import androidx.compose.material.icons.Icons
10+
import androidx.compose.material.icons.outlined.AddCircle
1011
import androidx.compose.material.icons.outlined.Clear
1112
import androidx.compose.material3.ExperimentalMaterial3Api
1213
import androidx.compose.material3.Icon
14+
import androidx.compose.material3.IconButton
1315
import androidx.compose.material3.InputChip
1416
import androidx.compose.material3.MaterialTheme
1517
import androidx.compose.material3.Text
1618
import androidx.compose.runtime.Composable
1719
import androidx.compose.runtime.MutableState
18-
import androidx.compose.runtime.collectAsState
1920
import androidx.compose.runtime.mutableStateOf
20-
import androidx.compose.runtime.rememberCoroutineScope
21-
import androidx.compose.runtime.saveable.rememberSaveable
21+
import androidx.compose.runtime.remember
2222
import androidx.compose.ui.Modifier
23+
import androidx.compose.ui.res.dimensionResource
24+
import androidx.compose.ui.res.stringResource
2325
import androidx.compose.ui.unit.dp
24-
import androidx.hilt.navigation.compose.hiltViewModel
25-
import app.expense.presentation.viewModels.CategoriesViewModel
26-
import app.expense.tracker.ui.utils.AutoCompleteTextField
27-
import kotlinx.coroutines.launch
26+
import app.expense.tracker.R
27+
import app.expense.tracker.ui.utils.CategoryInputDialog
2828

2929
@OptIn(ExperimentalMaterial3Api::class)
3030
@Composable
3131
fun CategoryView(
32-
categories: MutableState<List<String>>,
33-
viewModel: CategoriesViewModel = hiltViewModel()
32+
categories: MutableState<List<String>>
3433
) {
35-
36-
val coroutineScope = rememberCoroutineScope()
37-
val suggestions = viewModel.categoriesState.collectAsState().value
38-
val category = rememberSaveable { mutableStateOf("") }
34+
val categoryDialogOpen = remember { mutableStateOf(false) }
3935

4036
Column {
41-
AutoCompleteTextField(
42-
value = category.value,
43-
label = "Category",
44-
suggestions = suggestions,
45-
onCategoryEntered = { value ->
46-
category.value = value
47-
coroutineScope.launch {
48-
viewModel.getCategories(value)
49-
}
50-
},
51-
onCategorySelect = { value ->
52-
categories.value = categories.value.toMutableList().apply { add(value.trim()) }
53-
category.value = ""
54-
},
55-
modifier = Modifier
56-
.fillMaxWidth()
57-
.padding(16.dp)
58-
)
37+
Row(
38+
modifier = Modifier.padding(
39+
start = dimensionResource(id = R.dimen.default_padding),
40+
end = dimensionResource(id = R.dimen.default_padding),
41+
top = dimensionResource(id = R.dimen.default_padding)
42+
)
43+
) {
44+
Text(
45+
text = stringResource(R.string.category),
46+
style = MaterialTheme.typography.titleMedium
47+
)
48+
Spacer(modifier = Modifier.weight(1f))
49+
IconButton(onClick = { categoryDialogOpen.value = true }) {
50+
Icon(
51+
imageVector = Icons.Outlined.AddCircle,
52+
contentDescription = stringResource(R.string.add_category)
53+
)
54+
}
55+
56+
if (categoryDialogOpen.value) {
57+
CategoryInputDialog(onCategoryEntered = { category ->
58+
categories.value = categories.value.toMutableList().apply {
59+
add(0, category)
60+
}
61+
categoryDialogOpen.value = false
62+
}, onDismiss = { categoryDialogOpen.value = false })
63+
}
64+
}
65+
5966
if (categories.value.isNotEmpty()) {
6067
Row(
6168
modifier = Modifier
62-
.padding(start = 16.dp, end = 16.dp, top = 4.dp)
69+
.padding(start = 16.dp, end = 16.dp)
6370
.horizontalScroll(rememberScrollState())
6471
) {
6572
categories.value.filter { it.isNotBlank() }.forEach { item ->
@@ -74,7 +81,7 @@ fun CategoryView(
7481
trailingIcon = {
7582
Icon(
7683
imageVector = Icons.Outlined.Clear,
77-
contentDescription = "Remove category"
84+
contentDescription = stringResource(id = R.string.remove_category)
7885
)
7986
}
8087
)

app/src/main/res/values/strings.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@
1818
<string name="amount">Amount</string>
1919
<string name="sms_permission_needed">Read SMS Permission is needed to get suggestion from Transaction Messages</string>
2020
<string name="request_permission">Request permission</string>
21+
<string name="category">Category</string>
22+
<string name="add_category">Add Category</string>
23+
<string name="cancel">Cancel</string>
24+
<string name="confirm">Confirm</string>
2125
</resources>

presentation/src/main/java/app/expense/presentation/viewModels/CategoriesViewModel.kt renamed to presentation/src/main/java/app/expense/presentation/viewModels/CategorySuggestionViewModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import kotlinx.coroutines.flow.StateFlow
88
import javax.inject.Inject
99

1010
@HiltViewModel
11-
class CategoriesViewModel @Inject constructor(
11+
class CategorySuggestionViewModel @Inject constructor(
1212
private val fetchCategoriesUseCase: FetchCategoriesUseCase,
1313
) : ViewModel() {
1414

0 commit comments

Comments
 (0)