From 3ae784df970679649d29224de0cb9067fd7a91fa Mon Sep 17 00:00:00 2001 From: Kirill Kamakin Date: Sat, 15 Jul 2023 21:55:06 +0200 Subject: [PATCH] Implement deletion of shopping list items (#163) * Disable unstable Gradle features * Implement deletion of shopping list items * Hide deleted items even before they're deleted * Check/uncheck items locally while the BE is updated --- .../datasource/v1/MealieDataSourceV1.kt | 2 + .../datasource/v1/MealieDataSourceV1Impl.kt | 8 + .../mealient/datasource/v1/MealieServiceV1.kt | 5 + .../network/ShoppingListsDataSource.kt | 2 + .../network/ShoppingListsDataSourceImpl.kt | 5 + .../shopping_lists/repo/ShoppingListsRepo.kt | 2 + .../repo/ShoppingListsRepoImpl.kt | 5 + .../shopping_lists/ui/ShoppingListScreen.kt | 161 +++++++++++------- .../ui/ShoppingListScreenState.kt | 7 +- .../ui/ShoppingListViewModel.kt | 52 ++++-- .../ui/composables/LazyColumnPullRefresh.kt | 14 +- .../composables/LazyColumnWithLoadingState.kt | 9 +- .../src/main/res/values/strings.xml | 1 + gradle.properties | 2 - 14 files changed, 192 insertions(+), 83 deletions(-) diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1.kt index 945c660..c24461c 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1.kt @@ -64,4 +64,6 @@ interface MealieDataSourceV1 { suspend fun getShoppingList(id: String): GetShoppingListResponseV1 suspend fun updateIsShoppingListItemChecked(id: String, isChecked: Boolean) + + suspend fun deleteShoppingListItem(id: String) } \ No newline at end of file diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt index 3c8ef5c..6150458 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieDataSourceV1Impl.kt @@ -188,4 +188,12 @@ class MealieDataSourceV1Impl @Inject constructor( } updateShoppingListItem(id, JsonObject(updatedItem)) } + + override suspend fun deleteShoppingListItem( + id: String, + ) = networkRequestWrapper.makeCallAndHandleUnauthorized( + block = { service.deleteShoppingListItem(id) }, + logMethod = { "deleteShoppingListItem" }, + logParameters = { "id = $id" } + ) } diff --git a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieServiceV1.kt b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieServiceV1.kt index bbb7c22..6a2ce27 100644 --- a/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieServiceV1.kt +++ b/datasource/src/main/kotlin/gq/kirmanak/mealient/datasource/v1/MealieServiceV1.kt @@ -89,4 +89,9 @@ interface MealieServiceV1 { @Path("id") id: String, @Body request: JsonElement, ) + + @DELETE("/api/groups/shopping/items/{id}") + suspend fun deleteShoppingListItem( + @Path("id") id: String, + ) } \ No newline at end of file diff --git a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/network/ShoppingListsDataSource.kt b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/network/ShoppingListsDataSource.kt index fa901b5..0cca61d 100644 --- a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/network/ShoppingListsDataSource.kt +++ b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/network/ShoppingListsDataSource.kt @@ -10,4 +10,6 @@ interface ShoppingListsDataSource { suspend fun getShoppingList(id: String): FullShoppingListInfo suspend fun updateIsShoppingListItemChecked(id: String, checked: Boolean) + + suspend fun deleteShoppingListItem(id: String) } \ No newline at end of file diff --git a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/network/ShoppingListsDataSourceImpl.kt b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/network/ShoppingListsDataSourceImpl.kt index a418974..6e59c55 100644 --- a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/network/ShoppingListsDataSourceImpl.kt +++ b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/network/ShoppingListsDataSourceImpl.kt @@ -24,5 +24,10 @@ class ShoppingListsDataSourceImpl @Inject constructor( id: String, checked: Boolean, ) = v1Source.updateIsShoppingListItemChecked(id, checked) + + override suspend fun deleteShoppingListItem( + id: String + ) = v1Source.deleteShoppingListItem(id) + } diff --git a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/repo/ShoppingListsRepo.kt b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/repo/ShoppingListsRepo.kt index 6d3bb86..c74f8cd 100644 --- a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/repo/ShoppingListsRepo.kt +++ b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/repo/ShoppingListsRepo.kt @@ -10,4 +10,6 @@ interface ShoppingListsRepo { suspend fun getShoppingLists(): List suspend fun getShoppingList(id: String): FullShoppingListInfo + + suspend fun deleteShoppingListItem(id: String) } \ No newline at end of file diff --git a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/repo/ShoppingListsRepoImpl.kt b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/repo/ShoppingListsRepoImpl.kt index b8ee758..89c23d0 100644 --- a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/repo/ShoppingListsRepoImpl.kt +++ b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/repo/ShoppingListsRepoImpl.kt @@ -25,4 +25,9 @@ class ShoppingListsRepoImpl @Inject constructor( logger.v { "getShoppingListItems() called with: id = $id" } return dataSource.getShoppingList(id) } + + override suspend fun deleteShoppingListItem(id: String) { + logger.v { "deleteShoppingListItem() called with: id = $id" } + dataSource.deleteShoppingListItem(id) + } } \ No newline at end of file diff --git a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListScreen.kt b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListScreen.kt index 3a11c50..9e4ddf4 100644 --- a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListScreen.kt +++ b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListScreen.kt @@ -1,16 +1,31 @@ package gq.kirmanak.mealient.shopping_lists.ui +import androidx.compose.animation.animateColorAsState +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Delete import androidx.compose.material3.Checkbox +import androidx.compose.material3.DismissDirection +import androidx.compose.material3.DismissValue import androidx.compose.material3.Divider +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.SwipeToDismiss import androidx.compose.material3.Text +import androidx.compose.material3.rememberDismissState import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource @@ -50,74 +65,114 @@ internal fun ShoppingListScreen( LazyColumnWithLoadingState( loadingState = loadingState.map { it.items }, + contentPadding = PaddingValues(Dimens.Medium), + verticalArrangement = Arrangement.spacedBy(Dimens.Medium), defaultEmptyListError = defaultEmptyListError, errorToShowInSnackbar = shoppingListViewModel.errorToShowInSnackbar, onRefresh = shoppingListViewModel::refreshShoppingList, - onSnackbarShown = shoppingListViewModel::onSnackbarShown, - lazyColumnContent = { items -> - val firstCheckedItemIndex = items.indexOfFirst { it.item.checked } + onSnackbarShown = shoppingListViewModel::onSnackbarShown + ) { items -> + val firstCheckedItemIndex = items.indexOfFirst { it.checked } - itemsIndexed(items) { index, item -> - ShoppingListItem( - shoppingListItem = item.item, - isDisabled = item.isDisabled, - showDivider = index == firstCheckedItemIndex && index != 0, - ) { isChecked -> - shoppingListViewModel.onItemCheckedChange(item.item, isChecked) + itemsIndexed(items, { _, item -> item.id }) { index, item -> + ShoppingListItem( + shoppingListItem = item, + showDivider = index == firstCheckedItemIndex && index != 0, + modifier = Modifier.background(MaterialTheme.colorScheme.surface), + onCheckedChange = { isChecked -> + shoppingListViewModel.onItemCheckedChange(item, isChecked) + }, + onDismissed = { + shoppingListViewModel.deleteShoppingListItem(item) } - } + ) } - ) + } } +@OptIn(ExperimentalMaterial3Api::class) @Composable fun ShoppingListItem( shoppingListItem: ShoppingListItemInfo, - isDisabled: Boolean, showDivider: Boolean, modifier: Modifier = Modifier, onCheckedChange: (Boolean) -> Unit = {}, + onDismissed: () -> Unit = {}, ) { - Column( - modifier = modifier - .fillMaxWidth() - .padding(top = Dimens.Small, end = Dimens.Small, start = Dimens.Small), - ) { - if (showDivider) { - Divider() + val dismissState = rememberDismissState( + confirmValueChange = { + if (it == DismissValue.DismissedToStart) { + onDismissed() + } + true } - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start, - ) { - Checkbox( - checked = shoppingListItem.checked, - onCheckedChange = onCheckedChange, - enabled = !isDisabled, - ) + ) + SwipeToDismiss( + state = dismissState, + background = { + if (dismissState.targetValue == DismissValue.DismissedToStart) { + val color by animateColorAsState(MaterialTheme.colorScheme.error) + val iconColor by animateColorAsState(MaterialTheme.colorScheme.onSurface) + Box( + modifier = Modifier + .fillMaxSize() + .background(color) + ) { + Icon( + imageVector = Icons.Default.Delete, + contentDescription = stringResource(R.string.shopping_list_screen_delete_icon_content_description), + tint = iconColor, + modifier = Modifier + .align(Alignment.CenterEnd) + .padding(end = Dimens.Small) + ) + } + } + }, + dismissContent = { + Column( + modifier = Modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.surface), + ) { + if (showDivider) { + Divider() + } + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Start, + ) { + Checkbox( + checked = shoppingListItem.checked, + onCheckedChange = onCheckedChange, + ) - val isFood = shoppingListItem.isFood - val quantity = shoppingListItem.quantity - .takeUnless { it == 0.0 } - .takeUnless { it == 1.0 && !isFood } - ?.let { DecimalFormat.getInstance().format(it) } - val text = listOfNotNull( - quantity, - shoppingListItem.unit.takeIf { isFood }, - shoppingListItem.food.takeIf { isFood }, - shoppingListItem.note, - ).filter { it.isNotBlank() }.joinToString(" ") + val isFood = shoppingListItem.isFood + val quantity = shoppingListItem.quantity + .takeUnless { it == 0.0 } + .takeUnless { it == 1.0 && !isFood } + ?.let { DecimalFormat.getInstance().format(it) } + val text = listOfNotNull( + quantity, + shoppingListItem.unit.takeIf { isFood }, + shoppingListItem.food.takeIf { isFood }, + shoppingListItem.note, + ).filter { it.isNotBlank() }.joinToString(" ") - Text(text = text) - } - } + Text(text = text) + } + } + }, + modifier = modifier, + directions = setOf(DismissDirection.EndToStart), + ) } @Composable @Preview fun PreviewShoppingListItemChecked() { AppTheme { - ShoppingListItem(shoppingListItem = PreviewData.milk, false, false) + ShoppingListItem(shoppingListItem = PreviewData.milk, false) } } @@ -125,23 +180,7 @@ fun PreviewShoppingListItemChecked() { @Preview fun PreviewShoppingListItemUnchecked() { AppTheme { - ShoppingListItem(shoppingListItem = PreviewData.blackTeaBags, false, false) - } -} - -@Composable -@Preview -fun PreviewShoppingListItemCheckedDisabled() { - AppTheme { - ShoppingListItem(shoppingListItem = PreviewData.milk, true, false) - } -} - -@Composable -@Preview -fun PreviewShoppingListItemUncheckedDisabled() { - AppTheme { - ShoppingListItem(shoppingListItem = PreviewData.blackTeaBags, true, false) + ShoppingListItem(shoppingListItem = PreviewData.blackTeaBags, false) } } diff --git a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListScreenState.kt b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListScreenState.kt index e6df4d3..2735bf5 100644 --- a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListScreenState.kt +++ b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListScreenState.kt @@ -4,10 +4,5 @@ import gq.kirmanak.mealient.datasource.models.ShoppingListItemInfo internal data class ShoppingListScreenState( val name: String, - val items: List, -) - -internal data class ShoppingListItemState( - val item: ShoppingListItemInfo, - val isDisabled: Boolean, + val items: List, ) diff --git a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListViewModel.kt b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListViewModel.kt index d6cc0b6..b01d107 100644 --- a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListViewModel.kt +++ b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/ShoppingListViewModel.kt @@ -39,7 +39,9 @@ internal class ShoppingListViewModel @Inject constructor( private val args: ShoppingListNavArgs = ShoppingListScreenDestination.argsFrom(savedStateHandle) - private val _disabledItemIds: MutableStateFlow> = MutableStateFlow(mutableSetOf()) + private val checkedOverride = MutableStateFlow>(mutableMapOf()) + + private val deletedItemIds = MutableStateFlow>(mutableSetOf()) private val loadingHelper = loadingHelperFactory.create(viewModelScope) { shoppingListsRepo.getShoppingList(args.shoppingListId) @@ -47,7 +49,8 @@ internal class ShoppingListViewModel @Inject constructor( val loadingState: StateFlow> = combine( loadingHelper.loadingState, - _disabledItemIds, + checkedOverride, + deletedItemIds, ::buildLoadingState, ).stateIn(viewModelScope, SharingStarted.Eagerly, LoadingStateNoData.InitialLoad) @@ -64,7 +67,7 @@ internal class ShoppingListViewModel @Inject constructor( viewModelScope.launch { authRepo.isAuthorizedFlow.valueUpdatesOnly().collect { logger.d { "Authorization state changed to $it" } - if (it) refreshShoppingList() + if (it) doRefresh() } } } @@ -72,19 +75,25 @@ internal class ShoppingListViewModel @Inject constructor( fun refreshShoppingList() { logger.v { "refreshShoppingList() called" } viewModelScope.launch { - _errorToShowInSnackbar = loadingHelper.refresh().exceptionOrNull() + doRefresh() } } + private suspend fun doRefresh() { + _errorToShowInSnackbar = loadingHelper.refresh().exceptionOrNull() + } + private fun buildLoadingState( loadingState: LoadingState, - disabledItemIds: Set, + checkedOverrideMap: Map, + deletedItemIds: Set, ): LoadingState { - logger.v { "buildLoadingState() called with: loadingState = $loadingState, disabledItems = $disabledItemIds" } + logger.v { "buildLoadingState() called with: loadingState = $loadingState, checkedOverrideMap = $checkedOverrideMap, deletedItemIds = $deletedItemIds" } return loadingState.map { shoppingList -> val items = shoppingList.items + .filter { it.id !in deletedItemIds } + .map { it.copy(checked = checkedOverrideMap[it.id] ?: it.checked) } .sortedBy { it.checked } - .map { ShoppingListItemState(item = it, isDisabled = it.id in disabledItemIds) } ShoppingListScreenState(name = shoppingList.name, items = items) } } @@ -92,17 +101,22 @@ internal class ShoppingListViewModel @Inject constructor( fun onItemCheckedChange(item: ShoppingListItemInfo, isChecked: Boolean) { logger.v { "onItemCheckedChange() called with: item = $item, isChecked = $isChecked" } viewModelScope.launch { - _disabledItemIds.update { it + item.id } + checkedOverride.update { originalMap -> + originalMap.toMutableMap().also { newMap -> newMap[item.id] = isChecked } + } + checkedOverride.value[item.id] = isChecked val result = runCatchingExceptCancel { shoppingListsRepo.updateIsShoppingListItemChecked(item.id, isChecked) }.onFailure { logger.e(it) { "Failed to update item's checked state" } } - _disabledItemIds.update { it - item.id } _errorToShowInSnackbar = result.exceptionOrNull() if (result.isSuccess) { logger.v { "Item's checked state updated" } - refreshShoppingList() + doRefresh() + } + checkedOverride.update { originalMap -> + originalMap.toMutableMap().also { newMap -> newMap.remove(item.id) } } } } @@ -111,4 +125,22 @@ internal class ShoppingListViewModel @Inject constructor( logger.v { "onSnackbarShown() called" } _errorToShowInSnackbar = null } + + fun deleteShoppingListItem(item: ShoppingListItemInfo) { + logger.v { "deleteShoppingListItem() called with: item = $item" } + viewModelScope.launch { + deletedItemIds.update { it + item.id } + val result = runCatchingExceptCancel { + shoppingListsRepo.deleteShoppingListItem(item.id) + }.onFailure { + logger.e(it) { "Failed to delete item" } + } + _errorToShowInSnackbar = result.exceptionOrNull() + if (result.isSuccess) { + logger.v { "Item deleted" } + doRefresh() + } + deletedItemIds.update { it - item.id } + } + } } \ No newline at end of file diff --git a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/composables/LazyColumnPullRefresh.kt b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/composables/LazyColumnPullRefresh.kt index f6bb69c..d835231 100644 --- a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/composables/LazyColumnPullRefresh.kt +++ b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/composables/LazyColumnPullRefresh.kt @@ -1,6 +1,8 @@ package gq.kirmanak.mealient.shopping_lists.ui.composables +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListScope import androidx.compose.material.ExperimentalMaterialApi @@ -14,15 +16,21 @@ import androidx.compose.ui.Modifier @Composable @OptIn(ExperimentalMaterialApi::class) fun LazyColumnPullRefresh( - modifier: Modifier = Modifier, refreshState: PullRefreshState, isRefreshing: Boolean, - lazyColumnContent: LazyListScope.() -> Unit + contentPadding: PaddingValues, + verticalArrangement: Arrangement.Vertical, + lazyColumnContent: LazyListScope.() -> Unit, + modifier: Modifier = Modifier, ) { Box( modifier = modifier.pullRefresh(refreshState), ) { - LazyColumn(modifier = modifier, content = lazyColumnContent) + LazyColumn( + contentPadding = contentPadding, + verticalArrangement = verticalArrangement, + content = lazyColumnContent + ) PullRefreshIndicator( modifier = Modifier.align(Alignment.TopCenter), diff --git a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/composables/LazyColumnWithLoadingState.kt b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/composables/LazyColumnWithLoadingState.kt index 5b7d88a..f7efa29 100644 --- a/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/composables/LazyColumnWithLoadingState.kt +++ b/features/shopping_lists/src/main/kotlin/gq/kirmanak/mealient/shopping_lists/ui/composables/LazyColumnWithLoadingState.kt @@ -1,5 +1,7 @@ package gq.kirmanak.mealient.shopping_lists.ui.composables +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyListScope @@ -11,6 +13,7 @@ import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp import gq.kirmanak.mealient.shopping_lists.util.LoadingState import gq.kirmanak.mealient.shopping_lists.util.LoadingStateNoData import gq.kirmanak.mealient.shopping_lists.util.data @@ -24,6 +27,8 @@ fun LazyColumnWithLoadingState( loadingState: LoadingState>, defaultEmptyListError: String, modifier: Modifier = Modifier, + contentPadding: PaddingValues = PaddingValues(0.dp), + verticalArrangement: Arrangement.Vertical = Arrangement.Top, errorToShowInSnackbar: Throwable? = null, onSnackbarShown: () -> Unit = {}, onRefresh: () -> Unit = {}, @@ -61,10 +66,12 @@ fun LazyColumnWithLoadingState( else -> { LazyColumnPullRefresh( - modifier = innerModifier, refreshState = refreshState, isRefreshing = loadingState.isRefreshing, + contentPadding = contentPadding, + verticalArrangement = verticalArrangement, lazyColumnContent = { lazyColumnContent(list) }, + modifier = innerModifier, ) ErrorSnackbar( diff --git a/features/shopping_lists/src/main/res/values/strings.xml b/features/shopping_lists/src/main/res/values/strings.xml index 1d6eecb..08fc4f0 100644 --- a/features/shopping_lists/src/main/res/values/strings.xml +++ b/features/shopping_lists/src/main/res/values/strings.xml @@ -3,6 +3,7 @@ Shopping cart Unknown error %1$s is empty + Delete No shopping lists found Authentication is required No server connection diff --git a/gradle.properties b/gradle.properties index f37218c..f460af7 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,8 +1,6 @@ org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g -org.gradle.configureondemand=true org.gradle.parallel=true org.gradle.caching=true -org.gradle.unsafe.configuration-cache=true android.useAndroidX=true android.enableJetifier=false kotlin.code.style=official