From 7a25b0708beca0a5240edd65f8ca6456e4c41934 Mon Sep 17 00:00:00 2001 From: Reimar Date: Tue, 13 May 2025 21:15:06 +0200 Subject: [PATCH] Implement editing shopping items --- .../easyeat/helpers/shopping_list.kt | 11 +- .../easyeat/models/ShoppingListItem.kt | 8 +- .../ui/shopping_list/ShoppingListFragment.kt | 154 ++++++++++++------ .../src/main/res/drawable/ic_delete_24px.xml | 10 -- ...list.xml => dialog_edit_shopping_item.xml} | 0 .../res/menu/shopping_item_context_menu.xml | 5 +- app/app/src/main/res/values/strings.xml | 2 + 7 files changed, 120 insertions(+), 70 deletions(-) delete mode 100644 app/app/src/main/res/drawable/ic_delete_24px.xml rename app/app/src/main/res/layout/{dialog_add_to_shopping_list.xml => dialog_edit_shopping_item.xml} (100%) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt index a9adbc8..e6af1db 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/shopping_list.kt @@ -8,13 +8,12 @@ fun getShoppingList(ctx: Context): Array { return requestJson>(ctx, "GET", "/api/ShoppingList/get", null) } -@Serializable -data class AddShoppingItemRequest(val name: String, val amount: Double?, val unit: String?, val checked: Boolean) +fun addShoppingItem(ctx: Context, item: ShoppingListItem): ShoppingListItem { + return requestJson(ctx, "POST", "/api/ShoppingList/add", item) +} -fun addShoppingItem(ctx: Context, name: String, amount: Double?, unit: String?): ShoppingListItem { - val request = AddShoppingItemRequest(name, amount, unit, false) - - return requestJson(ctx, "POST", "/api/ShoppingList/add", request) +fun editShoppingItem(ctx: Context, old: ShoppingListItem, new: ShoppingListItem) { + requestJson(ctx, "PUT", "/api/ShoppingList/update?itemId=${old.id}", new) } fun toggleShoppingItemChecked(ctx: Context, item: ShoppingListItem) { diff --git a/app/app/src/main/java/tech/mercantec/easyeat/models/ShoppingListItem.kt b/app/app/src/main/java/tech/mercantec/easyeat/models/ShoppingListItem.kt index a97a6a7..f9d5711 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/models/ShoppingListItem.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/models/ShoppingListItem.kt @@ -3,4 +3,10 @@ package tech.mercantec.easyeat.models import kotlinx.serialization.Serializable @Serializable -data class ShoppingListItem(val id: Int, var name: String, var amount: Double?, var unit: String?, var checked: Boolean) +data class ShoppingListItem( + var id: Int? = null, + var name: String, + var amount: Double?, + var unit: String?, + var checked: Boolean = false, +) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt index 4c5f49f..2523ed8 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/ui/shopping_list/ShoppingListFragment.kt @@ -9,6 +9,7 @@ import android.widget.ArrayAdapter import android.widget.EditText import android.widget.PopupMenu import android.widget.Spinner +import android.widget.TextView import android.widget.Toast import androidx.fragment.app.Fragment import tech.mercantec.easyeat.R @@ -16,6 +17,7 @@ import tech.mercantec.easyeat.databinding.FragmentShoppingListBinding import tech.mercantec.easyeat.helpers.ApiRequestException import tech.mercantec.easyeat.helpers.addShoppingItem import tech.mercantec.easyeat.helpers.deleteShoppingItem +import tech.mercantec.easyeat.helpers.editShoppingItem import tech.mercantec.easyeat.helpers.getShoppingList import tech.mercantec.easyeat.helpers.toggleShoppingItemChecked import tech.mercantec.easyeat.models.ShoppingListItem @@ -86,11 +88,36 @@ class ShoppingListFragment : Fragment() { val popup = PopupMenu(requireActivity(), view) popup.apply { - menuInflater.inflate(R.menu.shopping_item_context_menu, menu) setOnMenuItemClickListener { when (it.itemId) { + R.id.edit_shopping_item -> { + showEditDialog(item) { dialog, newItem -> + thread { + try { + editShoppingItem(requireContext(), item, newItem) + } catch (e: ApiRequestException) { + activity?.runOnUiThread { + Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() + } + + return@thread + } finally { + dialog.dismiss() + } + + activity?.runOnUiThread { + val adapter = binding.shoppingList.adapter as ShoppingItemAdapter + + adapter.remove(item) + adapter.insert(newItem, position) + } + } + } + + true + } R.id.remove_shopping_item -> { (parent.adapter as ShoppingItemAdapter).remove(item) @@ -119,65 +146,88 @@ class ShoppingListFragment : Fragment() { // Show new item dialog when clicking add binding.addToShoppingList.setOnClickListener { - val view = requireActivity().layoutInflater.inflate(R.layout.dialog_add_to_shopping_list, null) - - val dialog = AlertDialog.Builder(activity) - .setView(view) - .setPositiveButton(R.string.add_label) { dialog, id -> - val dialog = dialog as AlertDialog - - val amount = view.findViewById(R.id.amount).text.toString().toDouble() - val unit = view.findViewById(R.id.unit_selector).selectedItem.toString().ifEmpty { null } - val name = view.findViewById(R.id.name).text.toString() - - dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false - dialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = false - - thread { - val item: ShoppingListItem - try { - item = addShoppingItem(requireContext(), name, amount, unit) - } catch (e: ApiRequestException) { - activity?.runOnUiThread { - Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() - } - - return@thread - } finally { - activity?.runOnUiThread { - dialog.dismiss() - } + showEditDialog(null) { dialog, item -> + thread { + val newItem: ShoppingListItem + try { + newItem = addShoppingItem(requireContext(), item) + } catch (e: ApiRequestException) { + activity?.runOnUiThread { + Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() } + return@thread + } finally { activity?.runOnUiThread { - val adapter = binding.shoppingList.adapter as ShoppingItemAdapter - - for (i in 0 ..< adapter.count) { - if (adapter.getItem(i)?.id == item.id) { - adapter.remove(adapter.getItem(i)) - adapter.insert(item, i) - - return@runOnUiThread - } - } - - adapter.insert(item, adapter.count) + dialog.dismiss() } } + + activity?.runOnUiThread { + val adapter = binding.shoppingList.adapter as ShoppingItemAdapter + + for (i in 0.. - dialog.cancel() - } - .create() - - dialog.show() - - val adapter = ArrayAdapter.createFromResource(requireContext(), R.array.units, android.R.layout.simple_spinner_item) - adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - - dialog.findViewById(R.id.unit_selector).adapter = adapter + } } return binding.root } + + private fun showEditDialog(item: ShoppingListItem?, onSave: (dialog: AlertDialog, item: ShoppingListItem) -> Unit) { + val view = requireActivity().layoutInflater.inflate(R.layout.dialog_edit_shopping_item, null) + + val dialog = AlertDialog.Builder(activity) + .setView(view) + .setPositiveButton(R.string.add_label) { dialog, id -> + val dialog = dialog as AlertDialog + + val amount = view.findViewById(R.id.amount).text.toString().toDouble() + val unit = view.findViewById(R.id.unit_selector).selectedItem.toString() + .ifEmpty { null } + val name = view.findViewById(R.id.name).text.toString() + + onSave(dialog, ShoppingListItem(item?.id, name, amount, unit, item?.checked ?: false)) + + dialog.getButton(AlertDialog.BUTTON_POSITIVE).isEnabled = false + dialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = false + + + } + .setNegativeButton(R.string.cancel_label) { dialog, _ -> + dialog.cancel() + } + .create() + + dialog.show() + + val adapter = ArrayAdapter.createFromResource( + requireContext(), + R.array.units, + android.R.layout.simple_spinner_item + ) + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + + dialog.findViewById(R.id.unit_selector).adapter = adapter + + // Pre-fill dialog inputs with current item if applicable + item?.let { item -> + view.findViewById(R.id.amount).setText(item.amount?.toBigDecimal()?.stripTrailingZeros().toString(), TextView.BufferType.EDITABLE) + view.findViewById(R.id.unit_selector).setSelection(adapter.getPosition(item.unit)) + view.findViewById(R.id.name).setText(item.name, TextView.BufferType.EDITABLE) + + view.findViewById(R.id.title).text = resources.getString(R.string.edit_shopping_item_label) + dialog.getButton(AlertDialog.BUTTON_POSITIVE).text = resources.getString(R.string.save_label) + } + } } \ No newline at end of file diff --git a/app/app/src/main/res/drawable/ic_delete_24px.xml b/app/app/src/main/res/drawable/ic_delete_24px.xml deleted file mode 100644 index d1ed443..0000000 --- a/app/app/src/main/res/drawable/ic_delete_24px.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - diff --git a/app/app/src/main/res/layout/dialog_add_to_shopping_list.xml b/app/app/src/main/res/layout/dialog_edit_shopping_item.xml similarity index 100% rename from app/app/src/main/res/layout/dialog_add_to_shopping_list.xml rename to app/app/src/main/res/layout/dialog_edit_shopping_item.xml diff --git a/app/app/src/main/res/menu/shopping_item_context_menu.xml b/app/app/src/main/res/menu/shopping_item_context_menu.xml index 48be440..14f1008 100644 --- a/app/app/src/main/res/menu/shopping_item_context_menu.xml +++ b/app/app/src/main/res/menu/shopping_item_context_menu.xml @@ -1,8 +1,11 @@ + diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml index a337f1a..bc68bda 100644 --- a/app/app/src/main/res/values/strings.xml +++ b/app/app/src/main/res/values/strings.xml @@ -32,6 +32,7 @@ Instructions Cancel Add shopping item + Edit shopping item Amount 500 Name @@ -40,6 +41,7 @@ Log out Checked Delete + Edit Your shopping list is empty Create Dish Generate recipe with AI