Various addtions to ShoppingList page (#325)
* Improve design of ShoppingListItems * Improve scrolling behaviour of ShoppingListItemEditor * Add search functionality to food and unit fields in ShoppingListItemEditor * Add functionality to display plurals of units in shopping lists * Fix tests * Ensure editor displays correctly when switching to food mode. - Fixed a bug causing the editor to display incorrectly when switching to food mode while at the bottom of the screen * Fix regressions caused in 5dff173 - Fixed a regression introducing incorrect scrolling behaviour when editing items at the bottom of the page - Fixed a regression causing the add button to not be hidden * Prefill fields in ShoppingListItemEditorFoodRow if possible * Remove unnecessary trigger for bringIntoView function - Remove unnecessary trigger for bringIntoView function of ShoppingListItemEditor * Display showAddButton dynamically * Add support for food plurals * Extract plural functionality to function * Remember filtering options * Use ExposedDropdownMenu instead of DropdownMenu - Updated selection of foods and units to use ExposedDropdownMenu - Updated composeBom to 2024.09.02 - Updated composeBom to 2024.09.02 to increment androidx.compose.material3 to 1.3.0 needed for androidx.compose.material3.MenuAnchorType * Only allow one edit menu to be open at a time
This commit is contained in:
@@ -12,4 +12,5 @@ data class GetFoodsResponse(
|
|||||||
data class GetFoodResponse(
|
data class GetFoodResponse(
|
||||||
@SerialName("name") val name: String,
|
@SerialName("name") val name: String,
|
||||||
@SerialName("id") val id: String,
|
@SerialName("id") val id: String,
|
||||||
|
@SerialName("pluralName") val pluralName: String? = null
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -11,5 +11,6 @@ data class GetUnitsResponse(
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class GetUnitResponse(
|
data class GetUnitResponse(
|
||||||
@SerialName("name") val name: String,
|
@SerialName("name") val name: String,
|
||||||
|
@SerialName("pluralName") val pluralName: String? = null,
|
||||||
@SerialName("id") val id: String
|
@SerialName("id") val id: String
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package gq.kirmanak.mealient.shopping_lists.ui.details
|
package gq.kirmanak.mealient.shopping_lists.ui.details
|
||||||
|
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -9,6 +10,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
|
import androidx.compose.foundation.relocation.BringIntoViewRequester
|
||||||
|
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
@@ -26,6 +29,7 @@ import androidx.compose.material3.HorizontalDivider
|
|||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
|
import androidx.compose.material3.MenuAnchorType
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -38,10 +42,12 @@ 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.focus.focusRequester
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.text.SpanStyle
|
||||||
|
import androidx.compose.ui.text.buildAnnotatedString
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.KeyboardType
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
|
import androidx.compose.ui.text.withStyle
|
||||||
import androidx.hilt.navigation.compose.hiltViewModel
|
import androidx.hilt.navigation.compose.hiltViewModel
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import gq.kirmanak.mealient.datasource.models.GetFoodResponse
|
import gq.kirmanak.mealient.datasource.models.GetFoodResponse
|
||||||
@@ -61,7 +67,6 @@ import gq.kirmanak.mealient.ui.util.LoadingState
|
|||||||
import gq.kirmanak.mealient.ui.util.data
|
import gq.kirmanak.mealient.ui.util.data
|
||||||
import gq.kirmanak.mealient.ui.util.error
|
import gq.kirmanak.mealient.ui.util.error
|
||||||
import gq.kirmanak.mealient.ui.util.map
|
import gq.kirmanak.mealient.ui.util.map
|
||||||
import kotlinx.coroutines.android.awaitFrame
|
|
||||||
import java.text.DecimalFormat
|
import java.text.DecimalFormat
|
||||||
|
|
||||||
data class ShoppingListNavArgs(
|
data class ShoppingListNavArgs(
|
||||||
@@ -118,6 +123,11 @@ private fun ShoppingListScreen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
var lastAddedItemIndex by remember { mutableIntStateOf(-1) }
|
var lastAddedItemIndex by remember { mutableIntStateOf(-1) }
|
||||||
|
// Show the add button only if there are no items being edited or added
|
||||||
|
val itemBeingEdited = !loadingState.data?.items.orEmpty().none {
|
||||||
|
(it as? ShoppingListItemState.ExistingItem)?.isEditing == true
|
||||||
|
|| it is ShoppingListItemState.NewItem
|
||||||
|
}
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
LaunchedEffect(lastAddedItemIndex) {
|
LaunchedEffect(lastAddedItemIndex) {
|
||||||
if (lastAddedItemIndex >= 0) lazyListState.animateScrollToItem(lastAddedItemIndex)
|
if (lastAddedItemIndex >= 0) lazyListState.animateScrollToItem(lastAddedItemIndex)
|
||||||
@@ -131,20 +141,23 @@ private fun ShoppingListScreen(
|
|||||||
contentPadding = PaddingValues(
|
contentPadding = PaddingValues(
|
||||||
start = Dimens.Medium,
|
start = Dimens.Medium,
|
||||||
end = Dimens.Medium,
|
end = Dimens.Medium,
|
||||||
top = Dimens.Medium,
|
top = Dimens.Large,
|
||||||
bottom = Dimens.Large * 4,
|
bottom = Dimens.Large,
|
||||||
),
|
),
|
||||||
verticalArrangement = Arrangement.spacedBy(Dimens.Medium),
|
verticalArrangement = Arrangement.spacedBy(Dimens.Medium),
|
||||||
snackbarText = errorToShowInSnackbar?.let { getErrorMessage(error = it) },
|
snackbarText = errorToShowInSnackbar?.let { getErrorMessage(error = it) },
|
||||||
onSnackbarShown = onSnackbarShown,
|
onSnackbarShown = onSnackbarShown,
|
||||||
onRefresh = onRefreshRequest,
|
onRefresh = onRefreshRequest,
|
||||||
floatingActionButton = {
|
floatingActionButton = {
|
||||||
|
// Only show the button if the editor is not active to avoid overlapping
|
||||||
|
if (!itemBeingEdited) {
|
||||||
FloatingActionButton(onClick = onAddItemClicked) {
|
FloatingActionButton(onClick = onAddItemClicked) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Add,
|
imageVector = Icons.Default.Add,
|
||||||
contentDescription = stringResource(id = R.string.shopping_list_screen_add_icon_content_description),
|
contentDescription = stringResource(id = R.string.shopping_list_screen_add_icon_content_description),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
lazyListState = lazyListState
|
lazyListState = lazyListState
|
||||||
) { sortedItems ->
|
) { sortedItems ->
|
||||||
@@ -178,7 +191,12 @@ private fun ShoppingListScreen(
|
|||||||
modifier = Modifier.background(MaterialTheme.colorScheme.surface),
|
modifier = Modifier.background(MaterialTheme.colorScheme.surface),
|
||||||
onCheckedChange = { onItemCheckedChange(itemState, it) },
|
onCheckedChange = { onItemCheckedChange(itemState, it) },
|
||||||
onDismissed = { onDeleteItem(itemState) },
|
onDismissed = { onDeleteItem(itemState) },
|
||||||
onEditStart = { onEditStart(itemState) },
|
onEditStart = {
|
||||||
|
// Only allow one item to be edited at a time
|
||||||
|
if (!itemBeingEdited) {
|
||||||
|
onEditStart(itemState)
|
||||||
|
}
|
||||||
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -186,7 +204,7 @@ private fun ShoppingListScreen(
|
|||||||
ShoppingListItemEditor(
|
ShoppingListItemEditor(
|
||||||
state = itemState.item,
|
state = itemState.item,
|
||||||
onEditCancelled = { onAddCancel(itemState) },
|
onEditCancelled = { onAddCancel(itemState) },
|
||||||
onEditConfirmed = { onAddConfirm(itemState) }
|
onEditConfirmed = { onAddConfirm(itemState) },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -217,30 +235,42 @@ fun ShoppingListSectionHeader(state: ShoppingListItemState.ItemLabel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ShoppingListItemEditor(
|
fun ShoppingListItemEditor(
|
||||||
state: ShoppingListItemEditorState,
|
state: ShoppingListItemEditorState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onEditCancelled: () -> Unit = {},
|
onEditCancelled: () -> Unit = {},
|
||||||
onEditConfirmed: () -> Unit = {},
|
onEditConfirmed: () -> Unit = {}
|
||||||
) {
|
) {
|
||||||
|
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||||
|
var shouldBringIntoView by remember { mutableStateOf(true) }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.bringIntoViewRequester(bringIntoViewRequester),
|
||||||
verticalArrangement = Arrangement.spacedBy(Dimens.Small),
|
verticalArrangement = Arrangement.spacedBy(Dimens.Small),
|
||||||
horizontalAlignment = Alignment.End,
|
horizontalAlignment = Alignment.End,
|
||||||
) {
|
) {
|
||||||
ShoppingListItemEditorFirstRow(
|
ShoppingListItemEditorFirstRow(state = state)
|
||||||
state = state
|
|
||||||
)
|
|
||||||
if (state.isFood) {
|
if (state.isFood) {
|
||||||
ShoppingListItemEditorFoodRow(state = state)
|
ShoppingListItemEditorFoodRow(state = state)
|
||||||
}
|
}
|
||||||
ShoppingListItemEditorButtonRow(
|
ShoppingListItemEditorButtonRow(
|
||||||
state = state,
|
state = state,
|
||||||
onEditCancelled = onEditCancelled,
|
onEditCancelled = onEditCancelled,
|
||||||
onEditConfirmed = onEditConfirmed
|
onEditConfirmed = onEditConfirmed,
|
||||||
|
// Bring editor back into view when the user switches between food and non-food items
|
||||||
|
onIconButtonClick = { shouldBringIntoView = true }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LaunchedEffect (shouldBringIntoView) {
|
||||||
|
bringIntoViewRequester.bringIntoView()
|
||||||
|
shouldBringIntoView = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -249,8 +279,6 @@ private fun ShoppingListItemEditorFirstRow(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val focusRequester = remember { FocusRequester() }
|
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
horizontalArrangement = Arrangement.spacedBy(Dimens.Small),
|
horizontalArrangement = Arrangement.spacedBy(Dimens.Small),
|
||||||
@@ -298,14 +326,8 @@ private fun ShoppingListItemEditorFirstRow(
|
|||||||
singleLine = true,
|
singleLine = true,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(3f, true)
|
.weight(3f, true)
|
||||||
.focusRequester(focusRequester),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(focusRequester) {
|
|
||||||
awaitFrame()
|
|
||||||
focusRequester.requestFocus()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -314,6 +336,7 @@ private fun ShoppingListItemEditorButtonRow(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onEditCancelled: () -> Unit = {},
|
onEditCancelled: () -> Unit = {},
|
||||||
onEditConfirmed: () -> Unit = {},
|
onEditConfirmed: () -> Unit = {},
|
||||||
|
onIconButtonClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
@@ -321,6 +344,7 @@ private fun ShoppingListItemEditorButtonRow(
|
|||||||
) {
|
) {
|
||||||
IconButton(onClick = {
|
IconButton(onClick = {
|
||||||
state.isFood = !state.isFood
|
state.isFood = !state.isFood
|
||||||
|
onIconButtonClick()
|
||||||
}) {
|
}) {
|
||||||
val stringId = if (state.isFood) {
|
val stringId = if (state.isFood) {
|
||||||
R.string.shopping_list_screen_editor_not_food_button
|
R.string.shopping_list_screen_editor_not_food_button
|
||||||
@@ -360,6 +384,9 @@ private fun ShoppingListItemEditorFoodRow(
|
|||||||
state: ShoppingListItemEditorState,
|
state: ShoppingListItemEditorState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
var foodSearchQuery by remember { mutableStateOf(state.food?.name ?: "") }
|
||||||
|
var unitSearchQuery by remember { mutableStateOf(state.unit?.name ?: "") }
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
horizontalArrangement = Arrangement.spacedBy(Dimens.Small),
|
horizontalArrangement = Arrangement.spacedBy(Dimens.Small),
|
||||||
@@ -370,10 +397,11 @@ private fun ShoppingListItemEditorFoodRow(
|
|||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = state.food?.name.orEmpty(),
|
value = foodSearchQuery,
|
||||||
onValueChange = { },
|
onValueChange = {
|
||||||
modifier = Modifier.menuAnchor(),
|
foodSearchQuery = it
|
||||||
readOnly = true,
|
},
|
||||||
|
modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryEditable, true),
|
||||||
textStyle = MaterialTheme.typography.bodyMedium,
|
textStyle = MaterialTheme.typography.bodyMedium,
|
||||||
label = {
|
label = {
|
||||||
Text(
|
Text(
|
||||||
@@ -388,11 +416,16 @@ private fun ShoppingListItemEditorFoodRow(
|
|||||||
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
|
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val foodFilteringOptions = remember(state.foods, foodSearchQuery) {
|
||||||
|
state.foods.filter {
|
||||||
|
it.name.contains(foodSearchQuery, ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
ExposedDropdownMenu(
|
ExposedDropdownMenu(
|
||||||
expanded = state.foodsExpanded,
|
expanded = state.foodsExpanded,
|
||||||
onDismissRequest = { state.foodsExpanded = false }
|
onDismissRequest = { state.foodsExpanded = false }
|
||||||
) {
|
) {
|
||||||
state.foods.forEach {
|
foodFilteringOptions.forEach {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = {
|
text = {
|
||||||
Text(text = it.name, style = MaterialTheme.typography.bodyMedium)
|
Text(text = it.name, style = MaterialTheme.typography.bodyMedium)
|
||||||
@@ -400,6 +433,7 @@ private fun ShoppingListItemEditorFoodRow(
|
|||||||
onClick = {
|
onClick = {
|
||||||
state.food = it
|
state.food = it
|
||||||
state.foodsExpanded = false
|
state.foodsExpanded = false
|
||||||
|
foodSearchQuery = it.name
|
||||||
},
|
},
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
if (it == state.food) {
|
if (it == state.food) {
|
||||||
@@ -421,10 +455,11 @@ private fun ShoppingListItemEditorFoodRow(
|
|||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = state.unit?.name.orEmpty(),
|
value = unitSearchQuery,
|
||||||
onValueChange = { },
|
onValueChange = {
|
||||||
modifier = Modifier.menuAnchor(),
|
unitSearchQuery = it
|
||||||
readOnly = true,
|
state.unitsExpanded = true },
|
||||||
|
modifier = Modifier.menuAnchor(MenuAnchorType.PrimaryEditable, true),
|
||||||
textStyle = MaterialTheme.typography.bodyMedium,
|
textStyle = MaterialTheme.typography.bodyMedium,
|
||||||
label = {
|
label = {
|
||||||
Text(
|
Text(
|
||||||
@@ -439,11 +474,16 @@ private fun ShoppingListItemEditorFoodRow(
|
|||||||
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
|
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val unitFilteringOptions = remember(state.foods, unitSearchQuery) {
|
||||||
|
state.units.filter {
|
||||||
|
it.name.contains(unitSearchQuery, ignoreCase = true)
|
||||||
|
}
|
||||||
|
}
|
||||||
ExposedDropdownMenu (
|
ExposedDropdownMenu (
|
||||||
expanded = state.unitsExpanded,
|
expanded = state.unitsExpanded,
|
||||||
onDismissRequest = { state.unitsExpanded = false }
|
onDismissRequest = { state.unitsExpanded = false }
|
||||||
) {
|
) {
|
||||||
state.units.forEach {
|
unitFilteringOptions.forEach {
|
||||||
DropdownMenuItem(
|
DropdownMenuItem(
|
||||||
text = {
|
text = {
|
||||||
Text(text = it.name, style = MaterialTheme.typography.bodyMedium)
|
Text(text = it.name, style = MaterialTheme.typography.bodyMedium)
|
||||||
@@ -451,6 +491,7 @@ private fun ShoppingListItemEditorFoodRow(
|
|||||||
onClick = {
|
onClick = {
|
||||||
state.unit = it
|
state.unit = it
|
||||||
state.unitsExpanded = false
|
state.unitsExpanded = false
|
||||||
|
unitSearchQuery = it.name
|
||||||
},
|
},
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
if (it == state.unit) {
|
if (it == state.unit) {
|
||||||
@@ -578,14 +619,68 @@ fun ShoppingListItem(
|
|||||||
.takeUnless { it == 0.0 }
|
.takeUnless { it == 0.0 }
|
||||||
.takeUnless { it == 1.0 && !isFood }
|
.takeUnless { it == 1.0 && !isFood }
|
||||||
?.let { DecimalFormat.getInstance().format(it) }
|
?.let { DecimalFormat.getInstance().format(it) }
|
||||||
val text = listOfNotNull(
|
|
||||||
quantity,
|
|
||||||
shoppingListItem.unit.takeIf { isFood }?.name,
|
|
||||||
shoppingListItem.food.takeIf { isFood }?.name,
|
|
||||||
shoppingListItem.note,
|
|
||||||
).filter { it.isNotBlank() }.joinToString(" ")
|
|
||||||
|
|
||||||
Text(text = text)
|
val primaryText = buildAnnotatedString {
|
||||||
|
fun appendWithSpace(text: String?) {
|
||||||
|
text?.let {
|
||||||
|
append(it)
|
||||||
|
append(" ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendBold(text: String?) {
|
||||||
|
text?.let {
|
||||||
|
withStyle(style = SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||||
|
append(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun appendWithPlural(
|
||||||
|
name: String,
|
||||||
|
pluralName: String?,
|
||||||
|
quantity: Double,
|
||||||
|
append: (String) -> Unit
|
||||||
|
) {
|
||||||
|
if (pluralName.isNullOrEmpty() || quantity <= 1) {
|
||||||
|
append(name)
|
||||||
|
} else {
|
||||||
|
append(pluralName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appendWithSpace(quantity)
|
||||||
|
if (!isFood) {
|
||||||
|
appendBold(shoppingListItem.note)
|
||||||
|
} else {
|
||||||
|
// Add plural unit and food name if available
|
||||||
|
shoppingListItem.unit?.let { unit ->
|
||||||
|
appendWithPlural(unit.name, unit.pluralName,
|
||||||
|
shoppingListItem.quantity, ::appendWithSpace)
|
||||||
|
}
|
||||||
|
shoppingListItem.food?.let { food ->
|
||||||
|
appendWithPlural(food.name, food.pluralName,
|
||||||
|
shoppingListItem.quantity, ::appendBold)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// only show note in secondary text if it's a food item due
|
||||||
|
// to the note already being displayed in the primary text otherwise
|
||||||
|
val secondaryText = shoppingListItem.takeIf { isFood }?.note.orEmpty()
|
||||||
|
|
||||||
|
Column {
|
||||||
|
Text(
|
||||||
|
text = primaryText,
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
)
|
||||||
|
if (secondaryText.isNotBlank()) {
|
||||||
|
Text(
|
||||||
|
text = secondaryText,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -642,7 +737,7 @@ private object PreviewData {
|
|||||||
isFood = true,
|
isFood = true,
|
||||||
note = "Cold",
|
note = "Cold",
|
||||||
quantity = 500.0,
|
quantity = 500.0,
|
||||||
unit = GetUnitResponse("ml", ""),
|
unit = GetUnitResponse(name= "ml", id= ""),
|
||||||
food = GetFoodResponse("Milk", ""),
|
food = GetFoodResponse("Milk", ""),
|
||||||
recipeReferences = listOf(
|
recipeReferences = listOf(
|
||||||
GetShoppingListItemRecipeReferenceResponse(
|
GetShoppingListItemRecipeReferenceResponse(
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ internal class ShoppingListViewModelTest : BaseUnitTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private val mlUnit = GetUnitResponse("ml", "")
|
private val mlUnit = GetUnitResponse(name="ml", id="")
|
||||||
|
|
||||||
private val milkLabel = GetItemLabelResponse("Milk", "#FF0000", "1", "0")
|
private val milkLabel = GetItemLabelResponse("Milk", "#FF0000", "1", "0")
|
||||||
private val milkFood = GetFoodResponse(name = "Milk", id ="")
|
private val milkFood = GetFoodResponse(name = "Milk", id ="")
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ androidXTestRunner = "1.6.2"
|
|||||||
androidXTestOrchestrator = "1.5.0"
|
androidXTestOrchestrator = "1.5.0"
|
||||||
junitKtx = "1.2.1"
|
junitKtx = "1.2.1"
|
||||||
# https://mvnrepository.com/artifact/androidx.compose/compose-bom
|
# https://mvnrepository.com/artifact/androidx.compose/compose-bom
|
||||||
composeBom = "2024.06.00"
|
composeBom = "2024.09.02"
|
||||||
# https://google.github.io/accompanist/
|
# https://google.github.io/accompanist/
|
||||||
accompanistVersion = "0.34.0"
|
accompanistVersion = "0.34.0"
|
||||||
# https://developer.android.com/jetpack/androidx/releases/compose-material
|
# https://developer.android.com/jetpack/androidx/releases/compose-material
|
||||||
|
|||||||
Reference in New Issue
Block a user