From 39bcdd9a9bc93d71b60f23248370b1f2632fede7 Mon Sep 17 00:00:00 2001 From: Reimar Date: Thu, 8 May 2025 10:33:28 +0200 Subject: [PATCH 1/3] Implement deleting shopping items --- .../easyeat/helpers/shopping_list.kt | 4 ++ .../ui/shopping_list/ShoppingListFragment.kt | 39 +++++++++++++++++++ .../src/main/res/drawable/ic_delete_24px.xml | 10 +++++ .../res/menu/shopping_item_context_menu.xml | 8 ++++ app/app/src/main/res/values/strings.xml | 1 + 5 files changed, 62 insertions(+) create mode 100644 app/app/src/main/res/drawable/ic_delete_24px.xml create mode 100644 app/app/src/main/res/menu/shopping_item_context_menu.xml 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 aa42d3e..73ff1bb 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 @@ -20,3 +20,7 @@ fun addShoppingItem(ctx: Context, name: String, amount: Double?, unit: String?) fun toggleShoppingItemChecked(ctx: Context, item: ShoppingListItem) { requestJson(ctx, "PUT", "/api/ShoppingList/check?itemId=${item.id}", null) } + +fun deleteShoppingItem(ctx: Context, item: ShoppingListItem) { + requestJson(ctx, "DELETE", "/api/ShoppingList/delete?itemId=${item.id}", null) +} 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 b1e9dc3..fee97e3 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 @@ -8,6 +8,7 @@ import android.view.View import android.view.ViewGroup import android.widget.ArrayAdapter import android.widget.EditText +import android.widget.PopupMenu import android.widget.Spinner import android.widget.Toast import androidx.fragment.app.Fragment @@ -15,6 +16,7 @@ import tech.mercantec.easyeat.R 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.getShoppingList import tech.mercantec.easyeat.helpers.toggleShoppingItemChecked import tech.mercantec.easyeat.models.Dish @@ -38,6 +40,7 @@ class ShoppingListFragment : Fragment() { _binding = FragmentShoppingListBinding.inflate(inflater, container, false) val root: View = binding.root + // Fetch shopping list items thread { val items: Array try { @@ -55,6 +58,7 @@ class ShoppingListFragment : Fragment() { } } + // Check / uncheck when clicking shopping item binding.shoppingList.setOnItemClickListener { parent, view, position, id -> val item = parent.getItemAtPosition(position) as ShoppingListItem item.checked = !item.checked @@ -73,6 +77,41 @@ class ShoppingListFragment : Fragment() { adapter.insert(item, position) } + // Show context menu when long clicking shopping item + binding.shoppingList.setOnItemLongClickListener { parent, view, position, id -> + val item = parent.getItemAtPosition(position) as ShoppingListItem + + val popup = PopupMenu(requireActivity(), view) + popup.apply { + menuInflater.inflate(R.menu.shopping_item_context_menu, menu) + setOnMenuItemClickListener { + when (it.itemId) { + R.id.remove_shopping_item -> { + (parent.adapter as ShoppingItemAdapter).remove(item) + + thread { + try { + deleteShoppingItem(requireContext(), item) + } catch (e: ApiRequestException) { + activity?.runOnUiThread { + Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() + } + } + } + + true + } + else -> false + } + } + show() + } + + + return@setOnItemLongClickListener true + } + + // Show new item dialog when clicking add binding.addToShoppingList.setOnClickListener { val view = requireActivity().layoutInflater.inflate(R.layout.dialog_add_to_shopping_list, null) diff --git a/app/app/src/main/res/drawable/ic_delete_24px.xml b/app/app/src/main/res/drawable/ic_delete_24px.xml new file mode 100644 index 0000000..d1ed443 --- /dev/null +++ b/app/app/src/main/res/drawable/ic_delete_24px.xml @@ -0,0 +1,10 @@ + + + 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 new file mode 100644 index 0000000..48be440 --- /dev/null +++ b/app/app/src/main/res/menu/shopping_item_context_menu.xml @@ -0,0 +1,8 @@ + + + + diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml index 741c1d1..e4bf2ef 100644 --- a/app/app/src/main/res/values/strings.xml +++ b/app/app/src/main/res/values/strings.xml @@ -39,6 +39,7 @@ Change password Log out Checked + Delete g From fa877b2c5e4be797f37788f1d12c66589904a5c5 Mon Sep 17 00:00:00 2001 From: Reimar Date: Thu, 8 May 2025 10:47:10 +0200 Subject: [PATCH 2/3] Show shopping item immediately as it is added to the list --- .../main/java/tech/mercantec/easyeat/helpers/api.kt | 2 +- .../tech/mercantec/easyeat/helpers/shopping_list.kt | 4 ++-- .../easyeat/ui/shopping_list/ShoppingListFragment.kt | 10 +++++++++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/app/app/src/main/java/tech/mercantec/easyeat/helpers/api.kt b/app/app/src/main/java/tech/mercantec/easyeat/helpers/api.kt index c3297eb..28dfc6f 100644 --- a/app/app/src/main/java/tech/mercantec/easyeat/helpers/api.kt +++ b/app/app/src/main/java/tech/mercantec/easyeat/helpers/api.kt @@ -94,6 +94,6 @@ inline fun requestJson(ctx: Context, method: String, Log.e("EasyEat", e.message!!) Log.e("EasyEat", response.body) - throw ApiRequestException("Failed to parse response: $response", e) + throw ApiRequestException("Failed to parse response: ${response.body}", e) } } 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 73ff1bb..a9adbc8 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 @@ -11,10 +11,10 @@ fun getShoppingList(ctx: Context): Array { @Serializable data class AddShoppingItemRequest(val name: String, val amount: Double?, val unit: String?, val checked: Boolean) -fun addShoppingItem(ctx: Context, name: String, amount: Double?, unit: String?) { +fun addShoppingItem(ctx: Context, name: String, amount: Double?, unit: String?): ShoppingListItem { val request = AddShoppingItemRequest(name, amount, unit, false) - requestJson(ctx, "POST", "/api/ShoppingList/add", request) + return requestJson(ctx, "POST", "/api/ShoppingList/add", request) } fun toggleShoppingItemChecked(ctx: Context, item: ShoppingListItem) { 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 fee97e3..ba837e6 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 @@ -128,17 +128,25 @@ class ShoppingListFragment : Fragment() { dialog.getButton(AlertDialog.BUTTON_NEGATIVE).isEnabled = false thread { + val item: ShoppingListItem try { - addShoppingItem(requireContext(), name, amount, unit) + 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() } } + + activity?.runOnUiThread { + val adapter = binding.shoppingList.adapter as ShoppingItemAdapter + adapter.insert(item, adapter.count) + } } } .setNegativeButton(R.string.cancel_label) { dialog, _ -> From df0c9511000506a66385d5dbb4a57b9c0b7ffc1d Mon Sep 17 00:00:00 2001 From: Reimar Date: Thu, 8 May 2025 11:02:04 +0200 Subject: [PATCH 3/3] Add progress bar and empty message to shopping list --- .../ui/shopping_list/ShoppingListFragment.kt | 32 +++++++++---------- .../res/layout/fragment_shopping_list.xml | 24 ++++++++++++-- app/app/src/main/res/values/strings.xml | 1 + 3 files changed, 37 insertions(+), 20 deletions(-) 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 ba837e6..7fd4d67 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 @@ -1,7 +1,6 @@ package tech.mercantec.easyeat.ui.shopping_list import android.app.AlertDialog -import android.app.Dialog import android.os.Bundle import android.view.LayoutInflater import android.view.View @@ -19,26 +18,22 @@ import tech.mercantec.easyeat.helpers.addShoppingItem import tech.mercantec.easyeat.helpers.deleteShoppingItem import tech.mercantec.easyeat.helpers.getShoppingList import tech.mercantec.easyeat.helpers.toggleShoppingItemChecked -import tech.mercantec.easyeat.models.Dish import tech.mercantec.easyeat.models.ShoppingListItem import java.util.ArrayList import kotlin.concurrent.thread class ShoppingListFragment : Fragment() { - - private var _binding: FragmentShoppingListBinding? = null - - // This property is only valid between onCreateView and - // onDestroyView. - private val binding get() = _binding!! - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - _binding = FragmentShoppingListBinding.inflate(inflater, container, false) - val root: View = binding.root + val binding = FragmentShoppingListBinding.inflate(inflater, container, false) + + binding.shoppingList.visibility = View.GONE + binding.emptyShoppingList.visibility = View.GONE + binding.addToShoppingList.visibility = View.GONE + binding.loading.visibility = View.VISIBLE // Fetch shopping list items thread { @@ -48,12 +43,20 @@ class ShoppingListFragment : Fragment() { } catch (e: ApiRequestException) { activity?.runOnUiThread { Toast.makeText(context, e.message, Toast.LENGTH_LONG).show() + + binding.loading.visibility = View.GONE } return@thread } activity?.runOnUiThread { + binding.shoppingList.visibility = View.VISIBLE + binding.emptyShoppingList.visibility = View.VISIBLE + binding.addToShoppingList.visibility = View.VISIBLE + binding.loading.visibility = View.GONE + + binding.shoppingList.emptyView = binding.emptyShoppingList binding.shoppingList.adapter = ShoppingItemAdapter(requireContext(), ArrayList(items.toMutableList())) } } @@ -162,11 +165,6 @@ class ShoppingListFragment : Fragment() { dialog.findViewById(R.id.unit_selector).adapter = adapter } - return root - } - - override fun onDestroyView() { - super.onDestroyView() - _binding = null + return binding.root } } \ No newline at end of file diff --git a/app/app/src/main/res/layout/fragment_shopping_list.xml b/app/app/src/main/res/layout/fragment_shopping_list.xml index 17466ab..dab1872 100644 --- a/app/app/src/main/res/layout/fragment_shopping_list.xml +++ b/app/app/src/main/res/layout/fragment_shopping_list.xml @@ -1,10 +1,19 @@ - + + + + - + diff --git a/app/app/src/main/res/values/strings.xml b/app/app/src/main/res/values/strings.xml index e4bf2ef..3ea29e3 100644 --- a/app/app/src/main/res/values/strings.xml +++ b/app/app/src/main/res/values/strings.xml @@ -40,6 +40,7 @@ Log out Checked Delete + Your shopping list is empty g