Implement deleting dish, use popup menu for edit and delete
This commit is contained in:
parent
9c364b2493
commit
59683ea176
@ -19,7 +19,7 @@ class ApiRequestException(message: String, cause: Throwable?) : Exception(messag
|
|||||||
class HttpResponse(val body: String, val code: Int)
|
class HttpResponse(val body: String, val code: Int)
|
||||||
|
|
||||||
fun request(ctx: Context, method: String, path: String, data: String?, autoRefresh: Boolean = true): HttpResponse {
|
fun request(ctx: Context, method: String, path: String, data: String?, autoRefresh: Boolean = true): HttpResponse {
|
||||||
val url = URL(BuildConfig.API_LOCALHOST_URL + path)
|
val url = URL(BuildConfig.API_BASE_URL + path)
|
||||||
|
|
||||||
val prefs = ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE)
|
val prefs = ctx.getSharedPreferences("easyeat", Context.MODE_PRIVATE)
|
||||||
val authToken = prefs.getString("auth-token", null)
|
val authToken = prefs.getString("auth-token", null)
|
||||||
@ -67,7 +67,7 @@ fun request(ctx: Context, method: String, path: String, data: String?, autoRefre
|
|||||||
class HttpErrorResponse(val message: String)
|
class HttpErrorResponse(val message: String)
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
val json = Json { explicitNulls = false }
|
val json = Json { explicitNulls = false } // Treat missing values as null when decoding
|
||||||
|
|
||||||
inline fun <reified Req, reified Res> requestJson(ctx: Context, method: String, path: String, data: Req?): Res {
|
inline fun <reified Req, reified Res> requestJson(ctx: Context, method: String, path: String, data: Req?): Res {
|
||||||
val requestJson =
|
val requestJson =
|
||||||
|
@ -32,3 +32,6 @@ fun getRecipeDetails(ctx: Context, id: Int): Recipe {
|
|||||||
return requestJson<Unit, Recipe>(ctx, "GET", "/api/Recipe/get/$id", null)
|
return requestJson<Unit, Recipe>(ctx, "GET", "/api/Recipe/get/$id", null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteRecipe(ctx: Context, id: Int) {
|
||||||
|
requestJson<Recipe, Boolean>(ctx, "DELETE", "/api/Recipe/delete/$id", null)
|
||||||
|
}
|
||||||
|
@ -9,11 +9,7 @@ import android.widget.TextView
|
|||||||
import tech.mercantec.easyeat.R
|
import tech.mercantec.easyeat.R
|
||||||
import tech.mercantec.easyeat.models.DishListItem
|
import tech.mercantec.easyeat.models.DishListItem
|
||||||
|
|
||||||
class DishAdapter(
|
class DishAdapter(context: Context, private val dishes: List<DishListItem>) : ArrayAdapter<DishListItem>(context, 0, dishes) {
|
||||||
context: Context,
|
|
||||||
private val dishes: List<DishListItem>,
|
|
||||||
private val onDishClick: (DishListItem) -> Unit
|
|
||||||
) : ArrayAdapter<DishListItem>(context, 0, dishes) {
|
|
||||||
|
|
||||||
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
|
||||||
val dish = getItem(position)
|
val dish = getItem(position)
|
||||||
@ -25,10 +21,6 @@ class DishAdapter(
|
|||||||
nameTextView.text = dish?.name
|
nameTextView.text = dish?.name
|
||||||
descriptionTextView.text = dish?.description
|
descriptionTextView.text = dish?.description
|
||||||
|
|
||||||
view.setOnClickListener {
|
|
||||||
dish?.let { onDishClick(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,8 +26,6 @@ import tech.mercantec.easyeat.helpers.AddRecipeToShoppingList
|
|||||||
import tech.mercantec.easyeat.models.Ingredient
|
import tech.mercantec.easyeat.models.Ingredient
|
||||||
import tech.mercantec.easyeat.models.Recipe
|
import tech.mercantec.easyeat.models.Recipe
|
||||||
|
|
||||||
private lateinit var editRecipeLauncher: ActivityResultLauncher<Intent>
|
|
||||||
|
|
||||||
class DishDetailsActivity : AppCompatActivity() {
|
class DishDetailsActivity : AppCompatActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -40,12 +38,6 @@ class DishDetailsActivity : AppCompatActivity() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
editRecipeLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
|
||||||
loadRecipe(dishId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
findViewById<Button>(R.id.addDishToShoppingList).setOnClickListener {
|
findViewById<Button>(R.id.addDishToShoppingList).setOnClickListener {
|
||||||
val progressDialog = ProgressDialog(this)
|
val progressDialog = ProgressDialog(this)
|
||||||
progressDialog.setMessage("Loading...")
|
progressDialog.setMessage("Loading...")
|
||||||
@ -82,7 +74,6 @@ class DishDetailsActivity : AppCompatActivity() {
|
|||||||
val ingredientsContainer = findViewById<LinearLayout>(R.id.ingredients)
|
val ingredientsContainer = findViewById<LinearLayout>(R.id.ingredients)
|
||||||
val multiplierEditText = findViewById<EditText>(R.id.ingredient_multiplier)
|
val multiplierEditText = findViewById<EditText>(R.id.ingredient_multiplier)
|
||||||
val instructionsLayout = findViewById<LinearLayout>(R.id.instructions)
|
val instructionsLayout = findViewById<LinearLayout>(R.id.instructions)
|
||||||
val editRecipeBtn: Button = findViewById(R.id.editRecipeBtn)
|
|
||||||
|
|
||||||
thread {
|
thread {
|
||||||
val recipe: Recipe
|
val recipe: Recipe
|
||||||
@ -151,13 +142,6 @@ class DishDetailsActivity : AppCompatActivity() {
|
|||||||
displayIngredients(recipe.ingredients, multiplier)
|
displayIngredients(recipe.ingredients, multiplier)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
editRecipeBtn.setOnClickListener {
|
|
||||||
val jsonRecipe = Json.encodeToString(recipe)
|
|
||||||
val intent = Intent(this, EditDishActivity::class.java)
|
|
||||||
intent.putExtra("recipe_json", jsonRecipe)
|
|
||||||
editRecipeLauncher.launch(intent)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package tech.mercantec.easyeat.ui.dishes
|
package tech.mercantec.easyeat.ui.dishes
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.app.AlertDialog
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
@ -11,11 +12,15 @@ import android.widget.Toast
|
|||||||
import androidx.activity.result.ActivityResultLauncher
|
import androidx.activity.result.ActivityResultLauncher
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import kotlinx.serialization.encodeToString
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
import tech.mercantec.easyeat.R
|
import tech.mercantec.easyeat.R
|
||||||
import tech.mercantec.easyeat.databinding.FragmentDishesBinding
|
import tech.mercantec.easyeat.databinding.FragmentDishesBinding
|
||||||
import tech.mercantec.easyeat.helpers.ApiRequestException
|
import tech.mercantec.easyeat.helpers.ApiRequestException
|
||||||
import tech.mercantec.easyeat.helpers.GetAllRecipesResponse
|
import tech.mercantec.easyeat.helpers.GetAllRecipesResponse
|
||||||
|
import tech.mercantec.easyeat.helpers.deleteRecipe
|
||||||
import tech.mercantec.easyeat.helpers.getAllRecipes
|
import tech.mercantec.easyeat.helpers.getAllRecipes
|
||||||
|
import tech.mercantec.easyeat.helpers.getRecipeDetails
|
||||||
import tech.mercantec.easyeat.models.DishListItem
|
import tech.mercantec.easyeat.models.DishListItem
|
||||||
import tech.mercantec.easyeat.models.Recipe
|
import tech.mercantec.easyeat.models.Recipe
|
||||||
import kotlin.concurrent.thread
|
import kotlin.concurrent.thread
|
||||||
@ -23,7 +28,7 @@ import kotlin.concurrent.thread
|
|||||||
class DishesFragment : Fragment() {
|
class DishesFragment : Fragment() {
|
||||||
|
|
||||||
private var _binding: FragmentDishesBinding? = null
|
private var _binding: FragmentDishesBinding? = null
|
||||||
private lateinit var createDishLauncher: ActivityResultLauncher<Intent>
|
private lateinit var launcher: ActivityResultLauncher<Intent>
|
||||||
private val binding get() = _binding!!
|
private val binding get() = _binding!!
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
@ -36,6 +41,79 @@ class DishesFragment : Fragment() {
|
|||||||
|
|
||||||
loadRecipes()
|
loadRecipes()
|
||||||
|
|
||||||
|
// Show details on click
|
||||||
|
binding.dishesList.setOnItemClickListener { parent, view, position, id ->
|
||||||
|
val item = parent.getItemAtPosition(position) as DishListItem
|
||||||
|
|
||||||
|
val intent = Intent(requireContext(), DishDetailsActivity::class.java)
|
||||||
|
intent.putExtra("dish_id", item.id)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show context menu when long clicking shopping item
|
||||||
|
binding.dishesList.setOnItemLongClickListener { parent, view, position, id ->
|
||||||
|
val item = parent.getItemAtPosition(position) as DishListItem
|
||||||
|
|
||||||
|
val popup = PopupMenu(requireActivity(), view)
|
||||||
|
popup.apply {
|
||||||
|
menuInflater.inflate(R.menu.dish_context_menu, menu)
|
||||||
|
|
||||||
|
setOnMenuItemClickListener {
|
||||||
|
when (it.itemId) {
|
||||||
|
// Open edit dish activity when editing
|
||||||
|
R.id.edit_dish -> {
|
||||||
|
thread {
|
||||||
|
// Get full recipe details
|
||||||
|
val recipe: Recipe
|
||||||
|
try {
|
||||||
|
recipe = getRecipeDetails(requireContext(), item.id)
|
||||||
|
} catch (e: ApiRequestException) {
|
||||||
|
activity?.runOnUiThread {
|
||||||
|
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
return@thread
|
||||||
|
}
|
||||||
|
|
||||||
|
val intent = Intent(activity, EditDishActivity::class.java)
|
||||||
|
intent.putExtra("recipe_json", Json.encodeToString(recipe))
|
||||||
|
launcher.launch(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
R.id.remove_dish -> {
|
||||||
|
AlertDialog.Builder(activity)
|
||||||
|
.setTitle("Delete dish")
|
||||||
|
.setMessage("Are you sure you want to delete this dish?")
|
||||||
|
.setPositiveButton(R.string.delete_label) { _, _ ->
|
||||||
|
(parent.adapter as DishAdapter).remove(item)
|
||||||
|
|
||||||
|
thread {
|
||||||
|
try {
|
||||||
|
deleteRecipe(requireContext(), item.id)
|
||||||
|
} catch (e: ApiRequestException) {
|
||||||
|
activity?.runOnUiThread {
|
||||||
|
Toast.makeText(context, e.message, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.setNegativeButton(R.string.cancel_label, null)
|
||||||
|
.show()
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
show()
|
||||||
|
}
|
||||||
|
|
||||||
|
return@setOnItemLongClickListener true
|
||||||
|
}
|
||||||
|
|
||||||
binding.addDish.setOnClickListener {
|
binding.addDish.setOnClickListener {
|
||||||
val popup = PopupMenu(requireActivity(), it)
|
val popup = PopupMenu(requireActivity(), it)
|
||||||
|
|
||||||
@ -46,13 +124,13 @@ class DishesFragment : Fragment() {
|
|||||||
when (it.itemId) {
|
when (it.itemId) {
|
||||||
R.id.create_manually -> {
|
R.id.create_manually -> {
|
||||||
val intent = Intent(requireContext(), CreateDishActivity::class.java)
|
val intent = Intent(requireContext(), CreateDishActivity::class.java)
|
||||||
createDishLauncher.launch(intent)
|
launcher.launch(intent)
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
R.id.create_with_ai -> {
|
R.id.create_with_ai -> {
|
||||||
val intent = Intent(requireContext(), GenerateRecipeActivity::class.java)
|
val intent = Intent(requireContext(), GenerateRecipeActivity::class.java)
|
||||||
createDishLauncher.launch(intent)
|
launcher.launch(intent)
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -98,12 +176,7 @@ class DishesFragment : Fragment() {
|
|||||||
|
|
||||||
binding.dishesList.emptyView = binding.emptyDishesList
|
binding.dishesList.emptyView = binding.emptyDishesList
|
||||||
|
|
||||||
val adapter = DishAdapter(requireContext(), dishes) { selectedDish ->
|
val adapter = DishAdapter(requireContext(), dishes)
|
||||||
// Open details activity
|
|
||||||
val intent = Intent(requireContext(), DishDetailsActivity::class.java)
|
|
||||||
intent.putExtra("dish_id", selectedDish.id)
|
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
binding.dishesList.adapter = adapter
|
binding.dishesList.adapter = adapter
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +185,7 @@ class DishesFragment : Fragment() {
|
|||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
createDishLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
launcher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
|
||||||
if (result.resultCode == Activity.RESULT_OK) {
|
if (result.resultCode == Activity.RESULT_OK) {
|
||||||
loadRecipes()
|
loadRecipes()
|
||||||
}
|
}
|
||||||
|
@ -1,54 +0,0 @@
|
|||||||
package tech.mercantec.easyeat.ui.dishes
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.text.Html
|
|
||||||
import android.text.Html.FROM_HTML_MODE_LEGACY
|
|
||||||
import androidx.fragment.app.Fragment
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import android.widget.TextView
|
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import tech.mercantec.easyeat.R
|
|
||||||
import tech.mercantec.easyeat.models.Recipe
|
|
||||||
|
|
||||||
class RecipeFragment : Fragment() {
|
|
||||||
private var recipe: Recipe? = null
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
arguments?.let { args ->
|
|
||||||
recipe = args.getString("RECIPE")?.let { Json.decodeFromString<Recipe>(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateView(
|
|
||||||
inflater: LayoutInflater, container: ViewGroup?,
|
|
||||||
savedInstanceState: Bundle?,
|
|
||||||
): View? {
|
|
||||||
val binding = inflater.inflate(R.layout.fragment_recipe, container, false)
|
|
||||||
|
|
||||||
recipe?.let { recipe ->
|
|
||||||
binding.findViewById<TextView>(R.id.title).text = recipe.name
|
|
||||||
|
|
||||||
binding.findViewById<TextView>(R.id.ingredients).text =
|
|
||||||
Html.fromHtml(
|
|
||||||
"<ul>" +
|
|
||||||
recipe.ingredients.map { "<li>${it.amount} ${it.unit} ${it.name}</li>" } +
|
|
||||||
"</ul>",
|
|
||||||
FROM_HTML_MODE_LEGACY
|
|
||||||
)
|
|
||||||
|
|
||||||
binding.findViewById<TextView>(R.id.directions).text =
|
|
||||||
Html.fromHtml(
|
|
||||||
"<ul>" +
|
|
||||||
recipe.directions.map { "<li>${it}</li>" } +
|
|
||||||
"</ul>",
|
|
||||||
FROM_HTML_MODE_LEGACY
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return binding
|
|
||||||
}
|
|
||||||
}
|
|
@ -9,15 +9,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="30sp"
|
android:padding="30sp"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
<Button
|
|
||||||
android:id="@+id/editRecipeBtn"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="end"
|
|
||||||
android:backgroundTint="@color/cyan"
|
|
||||||
android:text="@string/edit_recipe"
|
|
||||||
android:tint="@android:color/white"
|
|
||||||
/>
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/dishDetailName"
|
android:id="@+id/dishDetailName"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -29,7 +29,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_centerInParent="true"
|
android:layout_centerInParent="true"
|
||||||
android:text="@string/empty_dishes_list"
|
android:text="@string/empty_dishes_list"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/add_dish"
|
android:id="@+id/add_dish"
|
||||||
|
@ -1,48 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:padding="30dp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/title"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:textSize="24sp"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:text="@string/ingredients_label"
|
|
||||||
style="@style/HighContrastText"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/ingredients"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_marginTop="20dp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:textStyle="bold"
|
|
||||||
android:text="@string/directions_label"
|
|
||||||
style="@style/HighContrastText"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/directions"
|
|
||||||
android:layout_marginTop="10dp"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
11
app/app/src/main/res/menu/dish_context_menu.xml
Normal file
11
app/app/src/main/res/menu/dish_context_menu.xml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item
|
||||||
|
android:id="@+id/edit_dish"
|
||||||
|
android:title="@string/edit_label"
|
||||||
|
/>
|
||||||
|
<item
|
||||||
|
android:id="@+id/remove_dish"
|
||||||
|
android:title="@string/delete_label"
|
||||||
|
/>
|
||||||
|
</menu>
|
@ -56,7 +56,6 @@
|
|||||||
<string name="instructions_label">Instructions</string>
|
<string name="instructions_label">Instructions</string>
|
||||||
<string name="increment_multiplier_desc">Increment portion count</string>
|
<string name="increment_multiplier_desc">Increment portion count</string>
|
||||||
<string name="decrement_multiplier_desc">Decrement portion size</string>
|
<string name="decrement_multiplier_desc">Decrement portion size</string>
|
||||||
<string name="edit_recipe">Edit Recipe</string>
|
|
||||||
<string name="empty_dishes_list">You have not created any dishes yet</string>
|
<string name="empty_dishes_list">You have not created any dishes yet</string>
|
||||||
<string name="allergies_label">Allergies</string>
|
<string name="allergies_label">Allergies</string>
|
||||||
<string name="allergies_hint">Lactose, Gluten</string>
|
<string name="allergies_hint">Lactose, Gluten</string>
|
||||||
|
Loading…
Reference in New Issue
Block a user