Merge branch 'master' of git.reim.ar:ReiMerc/easyeat

This commit is contained in:
Jeas0001 2025-05-13 10:32:02 +02:00
commit 1dd98e1a05
17 changed files with 205 additions and 67 deletions

View File

@ -44,6 +44,10 @@
android:name=".ui.dishes.CreateDishActivity"
android:exported="false" />
<activity
android:name=".ui.dishes.CreateDishAIActivity"
android:exported="false" />
<activity
android:name=".ui.profile.ChangePasswordActivity"
android:exported="false" />

View File

@ -37,7 +37,7 @@ fun request(ctx: Context, method: String, path: String, data: String?, autoRefre
outputStream.write(data.toByteArray())
outputStream.flush()
}
Log.i("http", responseCode.toString())
if (responseCode == 401) {
if (!autoRefresh || refreshToken == null || !refreshAuthToken(ctx, refreshToken)) {
val intent = Intent(ctx, LoginActivity::class.java)
@ -87,6 +87,14 @@ inline fun <reified Req, reified Res> requestJson(ctx: Context, method: String,
}
}
if (response.body.isBlank()) {
// Return Unit or an empty default value depending on Res
return when (Res::class) {
Unit::class -> Unit as Res
else -> throw ApiRequestException("Expected JSON but got empty response", null)
}
}
try {
return Json.decodeFromString<Res>(response.body)
} catch (e: SerializationException) {

View File

@ -1,8 +1,11 @@
package tech.mercantec.easyeat.helpers
import android.content.ClipDescription
import android.content.Context
import android.util.Log
import kotlinx.serialization.Serializable
import tech.mercantec.easyeat.models.Direction
import tech.mercantec.easyeat.models.Ingredient
import tech.mercantec.easyeat.models.Recipe
@Serializable
data class LoginRequest(val emailUsr: String, val password: String)
@ -109,12 +112,19 @@ fun changePassword(ctx: Context, oldPassword: String, newPassword: String) {
return requestJson<ChangePasswordRequest, Unit>(ctx, "PUT", "/api/User/change-password", request)
}
@Serializable
data class CreateRecipeRequest(val recipe: Recipe)
data class CreateRecipeRequest(val name: String, val description: String, val directions: List<Direction>, val ingredients: List<Ingredient>)
fun createRecipe(ctx: Context, recipe: Recipe) {
val request = CreateRecipeRequest(recipe)
return requestJson<CreateRecipeRequest, Unit>(ctx, "POST", "/api/recipe/create", request)
val request = CreateRecipeRequest(recipe.name, recipe.description, recipe.directions, recipe.ingredients)
requestJson<CreateRecipeRequest, Boolean>(ctx, "POST", "/api/recipe/create", request)
}
@Serializable
data class RecipeResponse(val id: Int, val name: String, val description: String)
fun getRecipies(ctx: Context): List<RecipeResponse> {
return requestJson<Unit, List<RecipeResponse>>(ctx, "GET", "/api/Recipe/getall", null)
}

View File

@ -1,3 +1,3 @@
package tech.mercantec.easyeat.models
public data class Dish(val name: String, val mainIngredient: String, val expense: Double)
public data class DishListItem(val id: Int,val name: String, val description: String)

View File

@ -34,10 +34,5 @@ class MainActivity : AppCompatActivity() {
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
findViewById<FloatingActionButton>(tech.mercantec.easyeat.R.id.add_dish).setOnClickListener {
val intent = Intent(this, CreateDishActivity::class.java)
startActivity(intent)
}
}
}

View File

@ -0,0 +1,16 @@
package tech.mercantec.easyeat.ui.dishes
import android.os.Bundle
import android.widget.LinearLayout
import androidx.appcompat.app.AppCompatActivity
import tech.mercantec.easyeat.R
class CreateDishAIActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.create_dish_ai_form)
}
}

View File

@ -1,5 +1,6 @@
package tech.mercantec.easyeat.ui.dishes
import android.app.Activity
import android.app.ProgressDialog
import android.content.Context
import android.os.Bundle
@ -60,16 +61,7 @@ class CreateDishActivity : AppCompatActivity() {
ingredients = ingredientList
)
Log.i("recipe name:", recipe.name)
Log.i("recipe name:", recipe.description)
for (x in recipe.directions) {
Log.i("recipe name:", x.instructions)
}
for(x in recipe.ingredients){
Log.i("recipe name:", x.name)
Log.i("recipe name:", x.amount.toString())
Log.i("recipe name:", x.unit.toString())
}
val progressDialog = ProgressDialog(this)
progressDialog.setMessage("Loading...")
progressDialog.show()
@ -92,15 +84,10 @@ class CreateDishActivity : AppCompatActivity() {
Toast.makeText(this, "Password changed successfully", Toast.LENGTH_LONG).show()
}
setResult(Activity.RESULT_OK)
finish()
}
}
}
private fun addIngredientRow() {

View File

@ -7,22 +7,20 @@ import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.TextView
import tech.mercantec.easyeat.R
import tech.mercantec.easyeat.models.Dish
import tech.mercantec.easyeat.models.DishListItem
class DishAdapter(context: Context, dishes: Array<Dish>) :
ArrayAdapter<Dish>(context, 0, dishes) {
class DishAdapter(context: Context, dishes: List<DishListItem>) :
ArrayAdapter<DishListItem>(context, 0, dishes) {
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val dish = getItem(position)
val view = convertView ?: LayoutInflater.from(context).inflate(R.layout.dish_list_item, parent, false)
val nameTextView = view.findViewById<TextView>(R.id.dishName)
val ingredientTextView = view.findViewById<TextView>(R.id.mainIngredient)
val expenseTextView = view.findViewById<TextView>(R.id.expense)
val descriptionTextView = view.findViewById<TextView>(R.id.descriptionTextView)
nameTextView.text = "Name: ${dish?.name}"
ingredientTextView.text = "Main Ingredient: ${dish?.mainIngredient}"
expenseTextView.text = "Expense: ${dish?.expense} kr"
descriptionTextView.text = "Description: ${dish?.description}"
return view
}

View File

@ -1,21 +1,33 @@
package tech.mercantec.easyeat.ui.dishes
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.Toast
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import tech.mercantec.easyeat.R
import tech.mercantec.easyeat.databinding.FragmentDishesBinding
import tech.mercantec.easyeat.models.Dish
import tech.mercantec.easyeat.helpers.ApiRequestException
import tech.mercantec.easyeat.helpers.RecipeResponse
import tech.mercantec.easyeat.helpers.getRecipies
import tech.mercantec.easyeat.helpers.login
import tech.mercantec.easyeat.models.DishListItem
import tech.mercantec.easyeat.ui.MainActivity
import kotlin.concurrent.thread
class DishesFragment : Fragment() {
private var _binding: FragmentDishesBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private lateinit var createDishLauncher: ActivityResultLauncher<Intent>
private val binding get() = _binding!!
override fun onCreateView(
@ -26,42 +38,72 @@ class DishesFragment : Fragment() {
_binding = FragmentDishesBinding.inflate(inflater, container, false)
val root: View = binding.root
binding.addDish.setOnClickListener {
val intent = Intent(requireContext(), CreateDishActivity::class.java)
startActivity(intent)
}
loadRecipes()
context?.let { context ->
binding.dishesList.setOnItemClickListener { parent, view, position, id ->
val selectedItem = parent.getItemAtPosition(position) as Dish
Toast.makeText(context, "you selected $selectedItem.name that costs ${selectedItem.expense} kr.", Toast.LENGTH_LONG).show()
binding.addDish.setOnClickListener {
val dialogView = LayoutInflater.from(requireContext()).inflate(R.layout.create_dish_modal_dialog, null)
val dialog = android.app.AlertDialog.Builder(requireContext())
.setView(dialogView)
.setCancelable(true) // tap outside to dismiss
.create()
dialog.window?.setBackgroundDrawableResource(android.R.color.transparent)
dialogView.findViewById<Button>(R.id.createManualBtn).setOnClickListener {
val intent = Intent(requireContext(), CreateDishActivity::class.java)
createDishLauncher.launch(intent)
dialog.dismiss()
}
val listItems = arrayOf(
Dish("Spaghetti Bolognese", "Beef", 70.5),
Dish("Margherita Pizza", "Cheese", 60.0),
Dish("Chicken Curry", "Chicken", 80.2),
Dish("Vegetable Stir Fry", "Mixed Vegetables", 50.5),
Dish("Sushi", "Fish", 100.0),
Dish("Beef Tacos", "Beef", 60.8),
Dish("Lentil Soup", "Lentils", 40.5),
Dish("Pasta Alfredo", "Cream", 60.9),
Dish("Caesar Salad", "Chicken", 50.8),
Dish("Falafel Wrap", "Chickpeas", 50.2)
)
dialogView.findViewById<Button>(R.id.createAIBtn).setOnClickListener {
val intent = Intent(requireContext(), CreateDishAIActivity::class.java)
createDishLauncher.launch(intent)
dialog.dismiss()
}
binding.dishesList.adapter = DishAdapter(context, listItems)
dialog.show()
}
return root
}
fun DishesFragment.loadRecipes() {
thread {
val recipes: List<RecipeResponse>
try {
recipes = getRecipies(requireContext())
} catch (e: ApiRequestException) {
activity?.runOnUiThread {
Toast.makeText(requireContext(), e.message, Toast.LENGTH_LONG).show()
}
return@thread
}
val dishes = recipes.map {
DishListItem(it.id, it.name, it.description)
}
activity?.runOnUiThread {
val adapter = DishAdapter(requireContext(), dishes)
binding.dishesList.adapter = adapter
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
createDishLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
loadRecipes()
}
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
}

View File

@ -0,0 +1,4 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="@android:color/white" />
<corners android:radius="16dp" />
</shape>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/formforai"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="FormForAI"
tools:layout_editor_absoluteX="126dp"
tools:layout_editor_absoluteY="287dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_root"
android:layout_width="250dp"
android:layout_height="400dp"
android:orientation="vertical"
android:background="@drawable/rounded_background"
android:padding="0dp"
android:gravity="center">
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:text="@string/create_dish"
android:textAlignment="center"
android:textSize="30sp"/>
<Button
android:id="@+id/createManualBtn"
android:layout_width="200dp"
android:layout_height="0dp"
android:layout_weight="1"
android:text="@string/manually" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#CCCCCC" />
<Button
android:id="@+id/createAIBtn"
android:layout_width="200dp"
android:layout_height="0dp"
android:layout_weight="1"
android:text="@string/search_for_dishes" />
</LinearLayout>

View File

@ -12,12 +12,8 @@
android:layout_height="wrap_content" />
<TextView
android:id="@+id/mainIngredient"
android:id="@+id/descriptionTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/expense"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@ -41,6 +41,9 @@
<string name="checked_desc">Checked</string>
<string name="delete_label">Delete</string>
<string name="empty_shopping_list">Your shopping list is empty</string>
<string name="search_for_dishes">Search For Dishes</string>
<string name="manually">Manually</string>
<string name="create_dish">Create Dish</string>
<string-array name="units">
<item></item>
<item>g</item>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<domain-config cleartextTrafficPermitted="true">
<domain includeSubdomains="true">localhost</domain>
</domain-config>
</network-security-config>

View File

@ -23,7 +23,14 @@ namespace API.BusinessLogic
var recipes = await _dbAccess.ReadRecipes(userId);
if (recipes == null || recipes.Count == 0) { return new ConflictObjectResult(new { message = "Could not find any recipes" }); }
return new OkObjectResult(recipes);
var recipeDtos = recipes.Select(r => new GetAllRecipesDTO
{
Id = r.Id,
Name = r.Name,
Description = r.Description
}).ToList();
return new OkObjectResult(recipeDtos);
}
// Gets a specifik with recipe with the ingredient and directions

View File

@ -0,0 +1,12 @@
namespace API.Models.RecipeModels
{
public class GetAllRecipesDTO
{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
}
}