Implement adding and modifying shopping list items (#165)
* Add dismissed shopping list item preview * Implement editing of note and quantity * Add new editor row for food * Implement loading units and foods * Display dropdown for foods * Display dropdown for units * Implement updating food and units * Create secondary editor state constructor * Display "Add" button * Combine editing state to an object * Implement showing editor for new items * Implement saving new items * Log final screen state * Fix ordering of foods * Show keyboard when editing starts * Add bottom padding to the list * Show new items above checked
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
package gq.kirmanak.mealient.datasource.models
|
||||
|
||||
data class FoodInfo(
|
||||
val name: String,
|
||||
val id: String
|
||||
)
|
||||
@@ -14,8 +14,8 @@ data class ShoppingListItemInfo(
|
||||
val isFood: Boolean,
|
||||
val note: String,
|
||||
val quantity: Double,
|
||||
val unit: String,
|
||||
val food: String,
|
||||
val unit: UnitInfo?,
|
||||
val food: FoodInfo?,
|
||||
val recipeReferences: List<ShoppingListItemRecipeReferenceInfo>,
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package gq.kirmanak.mealient.datasource.models
|
||||
|
||||
data class NewShoppingListItemInfo(
|
||||
val shoppingListId: String,
|
||||
val isFood: Boolean,
|
||||
val note: String,
|
||||
val quantity: Double,
|
||||
val unit: UnitInfo?,
|
||||
val food: FoodInfo?,
|
||||
val position: Int,
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
package gq.kirmanak.mealient.datasource.models
|
||||
|
||||
data class UnitInfo(
|
||||
val name: String,
|
||||
val id: String
|
||||
)
|
||||
@@ -1,12 +1,16 @@
|
||||
package gq.kirmanak.mealient.datasource.v1
|
||||
|
||||
import gq.kirmanak.mealient.datasource.models.ShoppingListItemInfo
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateRecipeRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateShoppingListItemRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetFoodsResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSummaryResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetShoppingListResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetShoppingListsResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetUnitsResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetUserInfoResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.ParseRecipeURLRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.UpdateRecipeRequestV1
|
||||
@@ -63,7 +67,13 @@ interface MealieDataSourceV1 {
|
||||
|
||||
suspend fun getShoppingList(id: String): GetShoppingListResponseV1
|
||||
|
||||
suspend fun updateIsShoppingListItemChecked(id: String, isChecked: Boolean)
|
||||
|
||||
suspend fun deleteShoppingListItem(id: String)
|
||||
|
||||
suspend fun updateShoppingListItem(item: ShoppingListItemInfo)
|
||||
|
||||
suspend fun getFoods(): GetFoodsResponseV1
|
||||
|
||||
suspend fun getUnits(): GetUnitsResponseV1
|
||||
|
||||
suspend fun addShoppingListItem(request: CreateShoppingListItemRequestV1)
|
||||
}
|
||||
@@ -3,14 +3,18 @@ package gq.kirmanak.mealient.datasource.v1
|
||||
import gq.kirmanak.mealient.datasource.NetworkError
|
||||
import gq.kirmanak.mealient.datasource.NetworkRequestWrapper
|
||||
import gq.kirmanak.mealient.datasource.decode
|
||||
import gq.kirmanak.mealient.datasource.models.ShoppingListItemInfo
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateApiTokenResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateRecipeRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.CreateShoppingListItemRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.ErrorDetailV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetFoodsResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetRecipeSummaryResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetShoppingListResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetShoppingListsResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetUnitsResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.GetUserInfoResponseV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.ParseRecipeURLRequestV1
|
||||
import gq.kirmanak.mealient.datasource.v1.models.UpdateRecipeRequestV1
|
||||
@@ -20,9 +24,7 @@ import kotlinx.serialization.json.Json
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.boolean
|
||||
import kotlinx.serialization.json.jsonObject
|
||||
import kotlinx.serialization.json.jsonPrimitive
|
||||
import retrofit2.HttpException
|
||||
import java.net.ConnectException
|
||||
import java.net.SocketTimeoutException
|
||||
@@ -175,20 +177,6 @@ class MealieDataSourceV1Impl @Inject constructor(
|
||||
logParameters = { "id = $id, request = $request" }
|
||||
)
|
||||
|
||||
override suspend fun updateIsShoppingListItemChecked(
|
||||
id: String,
|
||||
isChecked: Boolean
|
||||
) {
|
||||
// Has to be done in two steps because the API doesn't support updating the checked state
|
||||
val item = getShoppingListItem(id)
|
||||
val wasChecked = item.jsonObject.getValue("checked").jsonPrimitive.boolean
|
||||
if (wasChecked == isChecked) return
|
||||
val updatedItem = item.jsonObject.toMutableMap().apply {
|
||||
put("checked", JsonPrimitive(isChecked))
|
||||
}
|
||||
updateShoppingListItem(id, JsonObject(updatedItem))
|
||||
}
|
||||
|
||||
override suspend fun deleteShoppingListItem(
|
||||
id: String,
|
||||
) = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
@@ -196,4 +184,44 @@ class MealieDataSourceV1Impl @Inject constructor(
|
||||
logMethod = { "deleteShoppingListItem" },
|
||||
logParameters = { "id = $id" }
|
||||
)
|
||||
|
||||
override suspend fun updateShoppingListItem(
|
||||
item: ShoppingListItemInfo
|
||||
) {
|
||||
// Has to be done in two steps because we can't specify only the changed fields
|
||||
val remoteItem = getShoppingListItem(item.id)
|
||||
val updatedItem = remoteItem.jsonObject.toMutableMap().apply {
|
||||
put("checked", JsonPrimitive(item.checked))
|
||||
put("isFood", JsonPrimitive(item.isFood))
|
||||
put("note", JsonPrimitive(item.note))
|
||||
put("quantity", JsonPrimitive(item.quantity))
|
||||
put("foodId", JsonPrimitive(item.food?.id))
|
||||
put("unitId", JsonPrimitive(item.unit?.id))
|
||||
remove("unit")
|
||||
remove("food")
|
||||
}
|
||||
updateShoppingListItem(item.id, JsonObject(updatedItem))
|
||||
}
|
||||
|
||||
override suspend fun getFoods(): GetFoodsResponseV1 {
|
||||
return networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.getFoods(perPage = -1) },
|
||||
logMethod = { "getFoods" },
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun getUnits(): GetUnitsResponseV1 {
|
||||
return networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.getUnits(perPage = -1) },
|
||||
logMethod = { "getUnits" },
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun addShoppingListItem(
|
||||
request: CreateShoppingListItemRequestV1
|
||||
) = networkRequestWrapper.makeCallAndHandleUnauthorized(
|
||||
block = { service.createShoppingListItem(request) },
|
||||
logMethod = { "addShoppingListItem" },
|
||||
logParameters = { "request = $request" }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -94,4 +94,19 @@ interface MealieServiceV1 {
|
||||
suspend fun deleteShoppingListItem(
|
||||
@Path("id") id: String,
|
||||
)
|
||||
|
||||
@GET("/api/foods")
|
||||
suspend fun getFoods(
|
||||
@Query("perPage") perPage: Int,
|
||||
): GetFoodsResponseV1
|
||||
|
||||
@GET("/api/units")
|
||||
suspend fun getUnits(
|
||||
@Query("perPage") perPage: Int,
|
||||
): GetUnitsResponseV1
|
||||
|
||||
@POST("/api/groups/shopping/items")
|
||||
suspend fun createShoppingListItem(
|
||||
@Body request: CreateShoppingListItemRequestV1,
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
package gq.kirmanak.mealient.datasource.v1.models
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class CreateShoppingListItemRequestV1(
|
||||
@SerialName("shopping_list_id") val shoppingListId: String,
|
||||
@SerialName("checked") val checked: Boolean,
|
||||
@SerialName("position") val position: Int?,
|
||||
@SerialName("is_food") val isFood: Boolean,
|
||||
@SerialName("note") val note: String,
|
||||
@SerialName("quantity") val quantity: Double,
|
||||
@SerialName("food_id") val foodId: String?,
|
||||
@SerialName("unit_id") val unitId: String?,
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
package gq.kirmanak.mealient.datasource.v1.models
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class GetFoodsResponseV1(
|
||||
@SerialName("items") val items: List<GetFoodResponseV1>,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GetFoodResponseV1(
|
||||
@SerialName("name") val name: String,
|
||||
@SerialName("id") val id: String,
|
||||
)
|
||||
@@ -6,4 +6,5 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
data class GetRecipeIngredientFoodResponseV1(
|
||||
@SerialName("name") val name: String = "",
|
||||
@SerialName("id") val id: String = "",
|
||||
)
|
||||
@@ -6,4 +6,5 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
data class GetRecipeIngredientUnitResponseV1(
|
||||
@SerialName("name") val name: String = "",
|
||||
@SerialName("id") val id: String = "",
|
||||
)
|
||||
@@ -0,0 +1,15 @@
|
||||
package gq.kirmanak.mealient.datasource.v1.models
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class GetUnitsResponseV1(
|
||||
@SerialName("items") val items: List<GetUnitResponseV1>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GetUnitResponseV1(
|
||||
@SerialName("name") val name: String,
|
||||
@SerialName("id") val id: String
|
||||
)
|
||||
Reference in New Issue
Block a user