Merge pull request 'Integrate Endpoint/API/models for main functionallities' (#50) from models/integration into master
continuous-integration/drone/push Build is passing Details

Reviewed-on: ShopNCook/ShopNCook#50
pull/51/head
Maxime BATISTA 2 years ago
commit e2df22c858

@ -8,18 +8,18 @@ public partial class App : Application, ConnectionObserver, IApp
private IEndpoint Endpoint = new LocalEndpoint();
public UserNotifier Notifier => new ConsoleUserNotifier();
public IUserNotifier Notifier => new ConsoleUserNotifier();
public App()
{
InitializeComponent();
ForceLogin(); //start in login state
ForceLogin(); //start in login shell
}
public void OnAccountConnected(Account account)
{
Shell shell = new MainAppShell(account, this);
shell.GoToAsync("//Main");
Shell shell = new MainAppShell(account, Endpoint, this);
shell.GoToAsync("//Home");
MainPage = shell;
}

@ -7,7 +7,7 @@ using ShoopNCook.Pages;
public partial class ConnectAppShell : Shell
{
public ConnectAppShell(ConnectionObserver observer, IAccountManager accounts, UserNotifier notifier)
public ConnectAppShell(ConnectionObserver observer, IAccountManager accounts, IUserNotifier notifier)
{
ConnectionController controller = new ConnectionController(observer, accounts, notifier);
InitializeComponent();

@ -4,8 +4,15 @@
/// A notice reporter implementation that prints in console the applications's user notices.
/// </summary>
public class ConsoleUserNotifier :
UserNotifier
IUserNotifier
{
public void Success(string message)
{
Console.WriteLine("<User Notice> Success: " + message);
}
public void Error(string message)
{
Console.WriteLine("<User Notice> Error: " + message);

@ -7,8 +7,8 @@ namespace ShoopNCook.Controllers
{
private readonly ConnectionObserver observer;
private readonly IAccountManager accounts;
private readonly UserNotifier notifier;
public ConnectionController(ConnectionObserver observer, IAccountManager accounts, UserNotifier notifier) {
private readonly IUserNotifier notifier;
public ConnectionController(ConnectionObserver observer, IAccountManager accounts, IUserNotifier notifier) {
this.observer = observer;
this.accounts = accounts;
this.notifier = notifier;

@ -1,8 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Endpoint;
using Models;
using ShoopNCook.Pages;
namespace ShoopNCook.Controllers
{
@ -10,9 +8,14 @@ namespace ShoopNCook.Controllers
{
private readonly IApp app;
private readonly IEndpoint endpoint;
private readonly Account account;
public MorePageController(IApp app) {
public MorePageController(Account account, IEndpoint endpoint, IApp app)
{
this.app = app;
this.endpoint = endpoint;
this.account = account;
}
public void Logout()
@ -20,5 +23,15 @@ namespace ShoopNCook.Controllers
app.Notifier.Notice("You have been loged out.");
app.ForceLogin();
}
public void GoToMyRecipesPage()
{
Shell.Current.Navigation.PushAsync(new MyRecipesPage(account, endpoint.RecipesService, app.Notifier));
}
public void GoToProfilePage()
{
Shell.Current.Navigation.PushAsync(new ProfilePage(account));
}
}
}

@ -0,0 +1,23 @@
using Models;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LocalEndpoint
{
public interface IAccountOwnedRecipes
{
public Account Account { get; }
public bool UploadRecipe(Recipe recipe);
public bool RemoveRecipe(RecipeInfo info);
public ImmutableList<RecipeInfo> GetAccountRecipes();
}
}

@ -0,0 +1,25 @@
using Models;
using System.Collections.Immutable;
namespace Endpoint
{
public interface IAccountRecipesPreferences
{
public Account Account { get; }
public void AddToFavorites(RecipeInfo info);
public void RemoveFromFavorites(RecipeInfo info);
public void SetReviewScore(RecipeInfo info, uint score);
public bool AddToWeeklyList(RecipeInfo info, uint persAmount);
public AccountRecipeRate GetRate(RecipeInfo info);
public ImmutableList<RecipeInfo> GetFavorites();
public ImmutableList<RecipeInfo> GetRecommendedRecipes();
public ImmutableList<(RecipeInfo, uint)> GetWeeklyList();
}
}

@ -6,6 +6,8 @@ namespace Endpoint
{
public IAccountManager AccountManager { get; }
public IRecipesService RecipesService { get; }
}
}

@ -0,0 +1,23 @@
using LocalEndpoint;
using Models;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Endpoint
{
public interface IRecipesService
{
public ImmutableList<RecipeInfo> PopularRecipes();
public Recipe GetRecipe(RecipeInfo info);
public IAccountOwnedRecipes GetRecipesOf(Account account);
public IAccountRecipesPreferences GetPreferencesOf(Account account);
}
}

@ -9,7 +9,7 @@ namespace ShoopNCook
{
public interface IApp
{
public UserNotifier Notifier { get; }
public IUserNotifier Notifier { get; }
public void ForceLogin();
}

@ -6,9 +6,10 @@ using System.Threading.Tasks;
namespace ShoopNCook
{
public interface UserNotifier
public interface IUserNotifier
{
public void Success(string message);
public void Notice(string message);
public void Error(string message);

@ -0,0 +1,11 @@
using Endpoint;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LocalEndpoint
{
internal record AccountData(IAccountOwnedRecipes Recipes, IAccountRecipesPreferences Preferences);
}

@ -1,37 +0,0 @@
using Models;
using Endpoint;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LocalEndpoint
{
internal class AccountManager : IAccountManager
{
private static readonly Uri DEFAULT_ACCOUNT_IMAGE = new Uri("https://www.pngkey.com/png/full/115-1150152_default-profile-picture-avatar-png-green.png");
private Account userAccount = new Account(new User(DEFAULT_ACCOUNT_IMAGE, "Stub Account"), "test@example.com");
private string userPassword = "123456";
public Account? Login(string email, string password)
{
if (userAccount.Email == email && userPassword == password)
{
return userAccount;
}
return null;
}
public Account? Register(string email, string username, string password)
{
if (email == null || username == null || password == null)
return null;
userAccount = new Account(new User(DEFAULT_ACCOUNT_IMAGE, username), email);
userPassword = password;
return userAccount;
}
}
}

@ -0,0 +1,52 @@
using LocalEndpoint;
using Models;
using System.Collections.Immutable;
namespace Endpoint
{
internal class AccountOwnedRecipes : IAccountOwnedRecipes
{
public Account Account { get; init; }
private readonly Dictionary<Guid, Recipe> ownedRecipes = new Dictionary<Guid, Recipe>();
private readonly RecipesDatabase db;
public AccountOwnedRecipes(Account account, RecipesDatabase db)
{
Account = account;
this.db = db;
//Retrieve all owned recipes from database.
db.ListAll().ForEach(recipe =>
{
if (recipe.Owner == account.User) ownedRecipes[recipe.Info.Id] = recipe;
});
}
public bool UploadRecipe(Recipe recipe)
{
Guid id = recipe.Info.Id;
if (ownedRecipes.ContainsKey(id))
{
return false;
}
db.Insert(recipe);
ownedRecipes.Add(id, recipe);
return true;
}
public bool RemoveRecipe(RecipeInfo info)
{
db.Remove(info.Id);
return ownedRecipes.Remove(info.Id);
}
public ImmutableList<RecipeInfo> GetAccountRecipes()
{
return ownedRecipes.Values.ToImmutableList().ConvertAll(r => r.Info);
}
}
}

@ -0,0 +1,102 @@
using Endpoint;
using Models;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LocalEndpoint
{
internal class AccountRecipesPreferences : IAccountRecipesPreferences
{
//Binds a recipe's id to it's account review ratings.
private readonly Dictionary<Guid, AccountRecipeRate> ratings = new Dictionary<Guid, AccountRecipeRate>();
//Binds a recipe's id to its amount of person stored in the account's weekly list
private readonly Dictionary<Guid, uint> weekly = new Dictionary<Guid, uint>();
private readonly RecipesDatabase db;
public AccountRecipesPreferences(Account account, RecipesDatabase db)
{
Account = account;
this.db = db;
}
public Account Account { get; init; }
public ImmutableList<RecipeInfo> GetRecommendedRecipes()
{
return db.ListAll().ConvertAll(recipe => recipe.Info);
}
public ImmutableList<RecipeInfo> GetFavorites()
{
List<RecipeInfo> favorites = new List<RecipeInfo>();
foreach (Recipe recipe in db.ListAll())
{
if (ratings.TryGetValue(recipe.Info.Id, out AccountRecipeRate? rate))
{
if (rate.IsFavorite)
favorites.Add(recipe.Info);
}
}
return favorites.ToImmutableList();
}
public ImmutableList<(RecipeInfo, uint)> GetWeeklyList()
{
List<(RecipeInfo, uint)> weekly = new List<(RecipeInfo, uint)>();
foreach (Recipe recipe in db.ListAll())
{
if (this.weekly.TryGetValue(recipe.Info.Id, out uint personAmmount))
{
weekly.Add((recipe.Info, personAmmount));
}
}
return weekly.ToImmutableList();
}
public AccountRecipeRate GetRate(RecipeInfo info)
{
AccountRecipeRate rate = null;
if (!ratings.TryGetValue(info.Id, out rate))
{
rate = new AccountRecipeRate();
ratings.Add(info.Id, rate);
}
return rate;
}
public void AddToFavorites(RecipeInfo info)
{
AccountRecipeRate rate = GetRate(info);
ratings[info.Id] = new AccountRecipeRate(true, rate.Rate);
}
public bool AddToWeeklyList(RecipeInfo info, uint persAmount)
{
if (weekly.ContainsKey(info.Id))
return false;
weekly[info.Id] = persAmount;
return true;
}
public void RemoveFromFavorites(RecipeInfo info)
{
AccountRecipeRate rate = GetRate(info);
ratings[info.Id] = new AccountRecipeRate(false, rate.Rate);
}
public void SetReviewScore(RecipeInfo info, uint score)
{
AccountRecipeRate rate = GetRate(info);
ratings[info.Id] = new AccountRecipeRate(rate.IsFavorite, score);
}
}
}

@ -0,0 +1,33 @@
using Models;
using Endpoint;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace LocalEndpoint
{
internal class ConnectionManager : IAccountManager
{
private Account userAccount = Constants.MAIN_USER_ACCOUNT;
private string userPassword = Constants.MAIN_USER_PASSWORD;
public Account? Login(string email, string password)
{
if (Constants.MAIN_USER_ACCOUNT.Email == email && Constants.MAIN_USER_PASSWORD == password)
return Constants.MAIN_USER_ACCOUNT;
return null;
}
public Account? Register(string email, string username, string password)
{
if (email == null || username == null || password == null)
return null;
userAccount = new Account(new User(Constants.DEFAULT_ACCOUNT_IMAGE, username), email);
userPassword = password;
return userAccount;
}
}
}

@ -0,0 +1,20 @@
using Models;
namespace LocalEndpoint
{
internal class Constants
{
public static readonly Uri DEFAULT_ACCOUNT_IMAGE = new Uri("https://www.pngkey.com/png/full/115-1150152_default-profile-picture-avatar-png-green.png");
// User account for tests
public static readonly Account MAIN_USER_ACCOUNT = new Account(new User(DEFAULT_ACCOUNT_IMAGE, "Example Account"), "test@example.com");
public static readonly string MAIN_USER_PASSWORD = "123456";
// other user samples
public static readonly User USER1 = new User(new Uri("https://i.ibb.co/L6t6bGR/DALL-E-2023-05-10-20-27-31-cook-looking-at-the-camera-with-a-chef-s-hat-laughing-in-an-exaggerated-w.png"), "The Funny Chief");
public static readonly User USER2 = new User(DEFAULT_ACCOUNT_IMAGE, "Yanis");
public static readonly User USER3 = new User(DEFAULT_ACCOUNT_IMAGE, "Leo");
}
}

@ -1,10 +1,37 @@
using Endpoint;
using Models;
using System.Collections.Immutable;
namespace LocalEndpoint
{
/// <summary>
/// The local endpoint is an implementation of the Endpoint API definition.
///
/// </summary>
public class LocalEndpoint : IEndpoint
{
public IAccountManager AccountManager => new AccountManager();
private readonly IAccountManager accountManager = new ConnectionManager();
private readonly IRecipesService recipesService;
public LocalEndpoint()
{
RecipesDatabase db = new RecipesDatabase();
//miam
db.Insert(new Recipe(new RecipeInfo("Chicken Salad", 500, 20, new Uri("https://healthyfitnessmeals.com/wp-content/uploads/2021/04/Southwest-chicken-salad-7-500x500.jpg"), 4, Guid.NewGuid()), Constants.USER1, new List<Ingredient> { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List<PreparationStep> { new PreparationStep("Step 1", "Bake the eggs") }.ToImmutableList()));
db.Insert(new Recipe(new RecipeInfo("Chocolate Cake", 2500, 10, new Uri("https://bakewithshivesh.com/wp-content/uploads/2022/08/IMG_0248-scaled.jpg"), 3, Guid.NewGuid()), Constants.USER2, new List<Ingredient> { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List<PreparationStep> { new PreparationStep("Step 1", "Bake the eggs") }.ToImmutableList()));
db.Insert(new Recipe(new RecipeInfo("Salmon", 20, 10, new Uri("https://www.wholesomeyum.com/wp-content/uploads/2021/06/wholesomeyum-Pan-Seared-Salmon-Recipe-13.jpg"), 4, Guid.NewGuid()), Constants.MAIN_USER_ACCOUNT.User, new List<Ingredient> { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List<PreparationStep> { new PreparationStep("Step 1", "Bake the eggs") }.ToImmutableList()));
db.Insert(new Recipe(new RecipeInfo("Fish", 50, 30, new Uri("https://www.ciaanet.org/wp-content/uploads/2022/07/Atlantic-and-Pacific-whole-salmon-1024x683.jpg"), 4.5F, Guid.NewGuid()), Constants.MAIN_USER_ACCOUNT.User, new List<Ingredient> { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List<PreparationStep> { new PreparationStep("Step 1", "Bake the eggs") }.ToImmutableList()));
db.Insert(new Recipe(new RecipeInfo("Space Cake", 800, 5, new Uri("https://static.youmiam.com/images/recipe/1500x1000/space-cake-22706?placeholder=web_recipe&sig=f14a7a86da837c6b8cc678cde424d6d5902f99ec&v3"), 5, Guid.NewGuid()), Constants.USER3, new List<Ingredient> { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List<PreparationStep> { new PreparationStep("Step 1", "Bake the eggs") }.ToImmutableList()));
recipesService = new RecipesService(db);
}
public IAccountManager AccountManager => accountManager;
public IRecipesService RecipesService => recipesService;
}
}

@ -0,0 +1,43 @@
using Models;
using System.Collections.Generic;
using System.Collections.Immutable;
namespace LocalEndpoint
{
//Simple class to simulate a recipe database
internal class RecipesDatabase
{
private Dictionary<Guid, Recipe> recipes = new Dictionary<Guid, Recipe>();
public Recipe? Lookup(Guid id)
{
Recipe? recipe;
recipes.TryGetValue(id, out recipe);
return recipe;
}
public Recipe Get(Guid id)
{
return recipes[id];
}
public void Insert(Recipe recipe)
{
recipes[recipe.Info.Id] = recipe;
}
public void Remove(Guid id)
{
recipes.Remove(id);
}
public ImmutableList<Recipe> ListAll()
{
return recipes.Values.ToImmutableList();
}
}
}

@ -0,0 +1,55 @@
using Endpoint;
using Models;
using System.Collections.Immutable;
namespace LocalEndpoint
{
internal class RecipesService : IRecipesService
{
private readonly RecipesDatabase db;
private readonly Dictionary<Account, AccountData> accountsData = new Dictionary<Account, AccountData>();
public RecipesService(RecipesDatabase db)
{
this.db = db;
}
public ImmutableList<RecipeInfo> PopularRecipes()
{
return db.ListAll().Take(4).ToImmutableList().ConvertAll(v => v.Info);
}
public Recipe GetRecipe(RecipeInfo info)
{
return db.Get(info.Id);
}
public IAccountOwnedRecipes GetRecipesOf(Account account)
{
return GetOrInitData(account).Recipes;
}
public IAccountRecipesPreferences GetPreferencesOf(Account account)
{
return GetOrInitData(account).Preferences;
}
private AccountData GetOrInitData(Account account)
{
AccountData? data;
accountsData.TryGetValue(account, out data);
if (data == null)
{
AccountOwnedRecipes recipes = new AccountOwnedRecipes(account, db);
recipes.UploadRecipe(new Recipe(new RecipeInfo("Cupcake", 500, 12, new Uri("https://www.mycake.fr/wp-content/uploads/2015/12/rs_cupcake_4x3.jpg"), 4.2F, Guid.NewGuid()), account.User, new List<Ingredient> { new Ingredient("Chocolate", 4) }.ToImmutableList(), new List<PreparationStep> { new PreparationStep("Eat Chocolate", "Eat the chocolate") }.ToImmutableList()));
AccountRecipesPreferences preferences = new AccountRecipesPreferences(account, db);
data = new AccountData(recipes, preferences);
accountsData.Add(account, data);
}
return data;
}
}
}

@ -3,15 +3,15 @@ using Microsoft.Maui.Controls;
using Models;
using ShoopNCook.Controllers;
using ShoopNCook.Pages;
using Endpoint;
public partial class MainAppShell : Shell
{
public MainAppShell(Account account, IApp app)
public MainAppShell(Account account, IEndpoint endpoint, IApp app)
{
InitializeComponent();
HomeTab.ContentTemplate = new DataTemplate(() => new HomePage(account, app));
FavoritesTab.ContentTemplate = new DataTemplate(() => new FavoritesPage(account, app));
MyListTab.ContentTemplate = new DataTemplate(() => new MyListPage(account, app));
MoreTab.ContentTemplate = new DataTemplate(() => new MorePage(account, new MorePageController(app)));
HomeTab.ContentTemplate = new DataTemplate(() => new HomePage(account, app.Notifier, endpoint));
FavoritesTab.ContentTemplate = new DataTemplate(() => new FavoritesPage(account, app.Notifier, endpoint.RecipesService));
MyListTab.ContentTemplate = new DataTemplate(() => new MyListPage(account, app.Notifier, endpoint.RecipesService));
MoreTab.ContentTemplate = new DataTemplate(() => new MorePage(account, new MorePageController(account, endpoint, app)));
}
}

@ -1,15 +1,4 @@
namespace Models
{
public class Account
{
public Account(User usr, string mail)
{
User = usr;
Email = mail;
}
public User User { get; init; }
public string Email { get; init; }
}
public record Account(User User, string Email);
}

@ -0,0 +1,5 @@

namespace Models
{
public record AccountRecipeRate(bool IsFavorite = false, uint Rate = 0);
}

@ -1,17 +1,4 @@
namespace Models
{
public class Ingredient
{
public Ingredient(string name, float amount, Quantity quantity)
{
Name = name;
Amount = amount;
Quantity = quantity;
}
public string Name { get; init; }
public float Amount { get; init; }
public Quantity Quantity { get; init; }
}
public record Ingredient(string Name, float Amount);
}

@ -1,15 +1,5 @@
namespace Models
{
public class PreparationStep
{
public PreparationStep(string name, string description)
{
Name = name;
Description = description;
}
public string Name { get; init; }
public string Description { get; init; }
}
public record PreparationStep(string Name, string Description);
}

@ -1,6 +0,0 @@
namespace Models
{
public class Quantity
{
}
}

@ -1,25 +1,10 @@
namespace Models
{
public class Recipe
{
public RecipeInfo Info { get; init; }
public User Owner { get; init; }
using System.Collections.Immutable;
public List<Ingredient> Ingredients { get; init; }
public List<PreparationStep> Steps { get; init; }
public Recipe(
RecipeInfo info,
User owner,
List<Ingredient> ingredients,
List<PreparationStep> steps)
namespace Models
{
Info = info;
Owner = owner;
Ingredients = ingredients;
Steps = steps;
}
}
public record Recipe(
RecipeInfo Info,
User Owner,
ImmutableList<Ingredient> Ingredients,
ImmutableList<PreparationStep> Steps);
}

@ -0,0 +1,65 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Models
{
public class RecipeBuilder
{
private readonly string name;
private readonly User owner;
private uint callPerPers;
private uint cookTimeMins;
private Uri? image;
private List<Ingredient> ingredients = new List<Ingredient>();
private List<PreparationStep> steps = new List<PreparationStep>();
public RecipeBuilder(string name, User owner)
{
this.name = name;
this.owner = owner;
}
public RecipeBuilder SetCallPerPers(uint callPerPers)
{
this.callPerPers = callPerPers;
return this;
}
public RecipeBuilder SetCookTimeMins(uint cookTimeMins)
{
this.cookTimeMins = cookTimeMins;
return this;
}
public RecipeBuilder SetImage(Uri image)
{
this.image = image;
return this;
}
public RecipeBuilder AddIngredient(Ingredient ingredient)
{
this.ingredients.Add(ingredient);
return this;
}
public RecipeBuilder AddStep(PreparationStep step)
{
this.steps.Add(step);
return this;
}
public Recipe Build()
{
RecipeInfo info = new RecipeInfo(name, callPerPers, cookTimeMins, image, 0, Guid.NewGuid());
return new Recipe(info, owner, ingredients.ToImmutableList(), steps.ToImmutableList());
}
}
}

@ -1,18 +1,10 @@
namespace Models
{
public class RecipeInfo
{
public string Name { get; init; }
public string Description { get; init; }
public Uri Image { get; init; }
public float AverageNote { get; init; }
public RecipeInfo(string name, string description, Uri image, float averageNote)
{
Name = name;
Description = description;
Image = image;
AverageNote = averageNote;
}
}
public record RecipeInfo(
string Name,
uint CalPerPers,
uint CookTimeMins,
Uri? Image,
float AverageNote,
Guid Id);
}

@ -1,15 +1,4 @@
namespace Models
{
public class User
{
public User(Uri profilePicture, string name)
{
ProfilePicture = profilePicture;
Name = name;
}
public Uri ProfilePicture { get; init; }
public string Name { get; init; }
}
public record User(Uri ProfilePicture, string Name);
}

@ -196,9 +196,7 @@
</ItemGroup>
<ProjectExtensions>
<VisualStudio>
<UserProperties XamarinHotReloadDebuggerTimeoutExceptionShoopNCookHideInfoBar="True" XamarinHotReloadUnhandledDeviceExceptionShoopNCookHideInfoBar="True" />
</VisualStudio>
<VisualStudio><UserProperties XamarinHotReloadDebuggerTimeoutExceptionShoopNCookHideInfoBar="True" XamarinHotReloadGenericExceptionInfoBarShoopNCookHideInfoBar="True" XamarinHotReloadUnhandledDeviceExceptionShoopNCookHideInfoBar="True" /></VisualStudio>
</ProjectExtensions>
</Project>

@ -21,7 +21,8 @@
<Entry
Style="{StaticResource UserInput}"
Placeholder="Ingredient Name"
HeightRequest="40"/>
HeightRequest="40"
x:Name="NameEntry"/>
</Border>
<Border
Grid.Column="1"
@ -32,7 +33,9 @@
<Entry
Style="{StaticResource UserInput}"
Placeholder="Quantity"
HeightRequest="40"/>
HeightRequest="40"
Keyboard="Numeric"
x:Name="QuantityEntry"/>
</Border>
<Border
Grid.Column="2"
@ -44,7 +47,8 @@
Title="Unit"
TextColor="{StaticResource TextColorPrimary}"
TitleColor="{StaticResource TextColorSecondary}"
FontFamily="PoppinsMedium">
FontFamily="PoppinsMedium"
x:Name="UnitPicker">
<Picker.ItemsSource>
<x:Array Type="{x:Type x:String}">
<x:String>G</x:String>

@ -1,3 +1,5 @@
using Models;
namespace ShoopNCook.Views;
public partial class IngredientEntry : ContentView
@ -6,4 +8,17 @@ public partial class IngredientEntry : ContentView
{
InitializeComponent();
}
public Ingredient MakeValue()
{
float quantity;
if (!float.TryParse(QuantityEntry.Text, out quantity))
{
quantity = 0;
// TODO handle quantity text malformation by raising exception
}
return new Ingredient(NameEntry.Text, quantity);
}
}

@ -1,3 +1,5 @@
using Models;
namespace ShoopNCook.Views;
public partial class IngredientView : ContentView
@ -30,12 +32,13 @@ public partial class IngredientView : ContentView
set => SetValue(UnitProperty, value);
}
public IngredientView(string name, float quantity, string unit)
public IngredientView(Ingredient ingredient)
{
InitializeComponent();
Name = name;
Quantity = quantity;
Unit = unit;
Name = ingredient.Name;
Quantity = ingredient.Amount;
//TODO Unit implementation in IngredientView.xaml.cs
Unit = "TODO: Unit implementation in IngredientView.xaml.cs";
}
}

@ -12,13 +12,20 @@
MinimumHeightRequest="175"
MinimumWidthRequest="150"
RowDefinitions="*, Auto">
<Grid.GestureRecognizers>
<TapGestureRecognizer
Tapped="OnViewTapped"
NumberOfTapsRequired="1"/>
</Grid.GestureRecognizers>
<Border
Grid.Row="0"
Stroke="Transparent"
StrokeShape="RoundRectangle 20"
BackgroundColor="{StaticResource ImageBackground}">
<Grid>
<Image />
<Image x:Name="RecipeImage" />
<HorizontalStackLayout
x:Name="Stars"
VerticalOptions="End"
@ -60,7 +67,7 @@
>
<Grid.GestureRecognizers>
<TapGestureRecognizer
Tapped="TapGestureRecognizer_Tapped"
Tapped="OnRemoveButtonTapped"
NumberOfTapsRequired="1"/>
</Grid.GestureRecognizers>
<Image

@ -1,18 +1,32 @@
using Models;
namespace ShoopNCook.Views;
public partial class OwnedRecipeView : ContentView
{
public OwnedRecipeView() : this(5, "Title")
{ }
private readonly Action clickCallback;
private readonly Action removeCallback;
private readonly RecipeInfo recipeInfo;
public OwnedRecipeView(float note, string title)
public OwnedRecipeView(RecipeInfo info, Action onClickCallback, Action onRemoveCallback)
{
InitializeComponent();
Note = note;
Title = title;
if (info.Image != null)
RecipeImage.Source = ImageSource.FromUri(info.Image);
Note = info.AverageNote;
Title = info.Name;
this.recipeInfo = info;
this.clickCallback = onClickCallback;
this.removeCallback = onRemoveCallback;
}
public bool IsViewing(RecipeInfo info)
{
return recipeInfo == info;
}
public float Note
{
set => SetNote(value);
@ -25,20 +39,25 @@ public partial class OwnedRecipeView : ContentView
private void SetNote(float note)
{
int i = 1;
foreach (Image img in Stars.Children)
note = (uint)note; //truncate integer as we currently do not handle semi stars
foreach (Image img in Stars.Children.Reverse())
{
if (i <= note)
if (note > 0)
{
img.Opacity = 0;
i++;
img.Opacity = 1;
note--;
}
else img.Opacity = 0;
}
else img.Opacity = 1;
}
private void OnViewTapped(object sender, TappedEventArgs e)
{
clickCallback();
}
private void TapGestureRecognizer_Tapped(object sender, TappedEventArgs e)
private void OnRemoveButtonTapped(object sender, TappedEventArgs e)
{
Console.WriteLine("This is a test");
removeCallback();
}
}

@ -1,21 +1,29 @@
using ShoopNCook.Pages;
using Models;
namespace ShoopNCook.Views;
public partial class RecipeView : ContentView
{
public RecipeView(): this(5, "Title", "Subtitle")
{}
private readonly Action callback;
public RecipeView(float note, string title, string subtitle)
public RecipeView(RecipeInfo info, Action onClickCallback)
{
InitializeComponent();
Note = note;
Title = title;
Subtitle = subtitle;
if (info.Image != null)
RecipeImage.Source = ImageSource.FromUri(info.Image);
Note = info.AverageNote;
Title = info.Name;
Subtitle = info.CookTimeMins + " min";
callback = onClickCallback;
}
public float Note
{
set => SetNote(value);
@ -31,22 +39,21 @@ public partial class RecipeView : ContentView
set => SubtitleLabel.Text = value;
}
private void SetNote(float note)
{
int i = 1;
foreach (Image img in Stars.Children)
note = (uint)note; //truncate integer as we currently do not handle semi stars
foreach (Image img in Stars.Children.Reverse())
{
if (i <= note)
if (note > 0)
{
img.Opacity = 0;
i++;
img.Opacity = 1;
note--;
}
else img.Opacity = 1;
else img.Opacity = 0;
}
}
private async void OnRecipeTapped(object sender, EventArgs e)
private void OnRecipeTapped(object sender, EventArgs e)
{
await Shell.Current.Navigation.PushAsync(new RecipePage());
callback();
}
}

@ -22,7 +22,8 @@
MaxLength="10000"
Style="{StaticResource UserInput}"
AutoSize="TextChanges"
FontSize="15"/>
FontSize="15"
x:Name="StepEditor"/>
</Border>
</VerticalStackLayout>

@ -1,3 +1,5 @@
using Models;
namespace ShoopNCook.Views;
public partial class StepEntry : ContentView
@ -12,6 +14,11 @@ public partial class StepEntry : ContentView
Ordinal = ordinal;
}
public PreparationStep MakeStep()
{
return new PreparationStep("Step " + Ordinal, StepEditor.Text);
}
public uint Ordinal {
get => uint.Parse(OrdinalLabel.Text);
set => OrdinalLabel.Text = value.ToString();

@ -14,13 +14,18 @@
MinimumHeightRequest="250"
MinimumWidthRequest="150"
RowDefinitions="*, Auto">
<Grid.GestureRecognizers>
<TapGestureRecognizer Tapped="OnRecipeTapped"/>
</Grid.GestureRecognizers>
<Border
Grid.Row="0"
Stroke="Transparent"
StrokeShape="RoundRectangle 20"
BackgroundColor="{StaticResource ImageBackground}">
<Grid>
<Image />
<Image x:Name="RecipeImage"/>
<HorizontalStackLayout
x:Name="Stars"
VerticalOptions="End"
@ -51,8 +56,9 @@
<Label
TextColor="{StaticResource TextColorPrimary}"
x:Name="TitleLabel"/>
<Grid></Grid>
<views:CounterView CounterText="pers"/>
<views:CounterView
CounterText="pers"
x:Name="Counter"/>
</VerticalStackLayout>
</Grid>
</Border>

@ -1,16 +1,24 @@
using Models;
namespace ShoopNCook.Views;
public partial class StoredRecipeView : ContentView
{
public StoredRecipeView() : this(5, "Title")
{ }
private readonly Action<uint> clickCallback;
public StoredRecipeView(float note, string title)
public StoredRecipeView(RecipeInfo info, uint personCount, Action<uint> onClickCallback)
{
InitializeComponent();
Note = note;
Title = title;
if (info.Image != null)
RecipeImage.Source = ImageSource.FromUri(info.Image);
Note = info.AverageNote;
Title = info.Name;
clickCallback = onClickCallback;
Counter.Count = personCount;
}
public float Note
@ -23,19 +31,22 @@ public partial class StoredRecipeView : ContentView
set => TitleLabel.Text = value;
}
private void SetNote(float note)
{
int i = 1;
foreach (Image img in Stars.Children)
note = (uint)note; //truncate integer as we currently do not handle semi stars
foreach (Image img in Stars.Children.Reverse())
{
if (i <= note)
if (note > 0)
{
img.Opacity = 0;
i++;
img.Opacity = 1;
note--;
}
else img.Opacity = 0;
}
else img.Opacity = 1;
}
private void OnRecipeTapped(object sender, TappedEventArgs e)
{
clickCallback(Counter.Count);
}
}

@ -21,7 +21,8 @@
HeightRequest="50"
WidthRequest="50"
Source="arrow_back.svg"/>
Source="arrow_back.svg"
Clicked="OnBackButtonClicked"/>
</HorizontalStackLayout>
<Label
Margin="-40,10,0,0"
@ -78,7 +79,8 @@
BackgroundColor="{StaticResource ActionButton}"
FontFamily="PoppinsMedium"
TextColor="White"
Text="That's been my email"/>
Text="That's been my email"
/>
</Border>
</VerticalStackLayout>
</Grid>
@ -136,7 +138,8 @@
BackgroundColor="{StaticResource ActionButton}"
FontFamily="PoppinsMedium"
TextColor="White"
Text="Confirm my email"/>
Text="Confirm my email"
Clicked="OnRegiterButtonTapped"/>
</Border>
</VerticalStackLayout>

@ -6,4 +6,12 @@ public partial class ConfirmMail : ContentPage
{
InitializeComponent();
}
private async void OnRegiterButtonTapped(object sender, EventArgs e)
{
await Shell.Current.GoToAsync("//LoginPage");
}
private async void OnBackButtonClicked(object sender, EventArgs e)
{
await Navigation.PopAsync();
}
}

@ -56,6 +56,7 @@
VerticalOptions="End"
TranslationY="20"
TranslationX="-20">
<ImageButton
Source="edit.svg"
WidthRequest="30"/>
@ -66,7 +67,8 @@
<!--Ingredients-->
<Entry
Style="{StaticResource UserInput}"
Placeholder="Specify your recipe name"/>
Placeholder="Specify your recipe name"
x:Name="RecipeNameEntry"/>
<Label
Style="{StaticResource h2}"
Text="Ingredient list (for 1 person)"/>
@ -115,6 +117,21 @@
Text="minutes"/>
</HorizontalStackLayout>
<HorizontalStackLayout>
<Label
Style="{StaticResource h3}"
VerticalTextAlignment="Center"
Text="Estimated cal/persons: "/>
<Entry
Style="{StaticResource UserInput}"
Keyboard="Numeric"
x:Name="EnergyInput"/>
<Label
Style="{StaticResource h3}"
VerticalTextAlignment="Center"
Text="cal/pers"/>
</HorizontalStackLayout>
<!--Preparation entry steps list-->
<Label
Style="{StaticResource h2}"
@ -154,7 +171,8 @@
Grid.Row="3"
Style="{StaticResource UserButton}"
BackgroundColor="{StaticResource ActionButton}"
Text="Upload recipe"/>
Text="Upload recipe"
Clicked="OnUploadRecipeClicked"/>
</Grid>
</ContentPage>

@ -1,12 +1,21 @@
using Models;
using ShoopNCook.Views;
namespace ShoopNCook.Pages;
public partial class CreateRecipePage : ContentPage
{
public CreateRecipePage()
private User owner;
private Action<Recipe> onRecipeCreated;
private IUserNotifier notifier;
public CreateRecipePage(User owner, IUserNotifier notifier, Action<Recipe> onRecipeCreated)
{
InitializeComponent();
this.owner = owner;
this.onRecipeCreated = onRecipeCreated;
this.notifier = notifier;
}
private void OnAddIngredientTapped(object sender, TappedEventArgs e)
@ -18,8 +27,48 @@ public partial class CreateRecipePage : ContentPage
{
StepList.Children.Add(new StepEntry((uint) StepList.Children.Count() + 1));
}
private async void OnBackButtonClicked(object sender, EventArgs e)
private void OnBackButtonClicked(object sender, EventArgs e)
{
Navigation.PopAsync();
}
private void OnUploadRecipeClicked(object sender, EventArgs e)
{
await Navigation.PopAsync();
uint callPerPers;
uint cookTimeMins;
bool hadErrors = false;
if (!uint.TryParse(EnergyInput.Text, out callPerPers))
{
hadErrors = true;
//TODO change EnergyInput background to red.
}
if (!uint.TryParse(CookTimeInput.Text, out cookTimeMins))
{
hadErrors = true;
//TODO change CookTimeInput background to red.
}
if (hadErrors)
{
notifier.Error("You need to fix input errors before upload.");
return;
}
RecipeBuilder builder = new RecipeBuilder(RecipeNameEntry.Text, owner)
.SetCallPerPers(callPerPers)
.SetCookTimeMins(cookTimeMins)
//TODO .SetImage(RecipeImage)
;
foreach (IngredientEntry entry in IngredientList.Children)
builder.AddIngredient(entry.MakeValue());
foreach (StepEntry entry in StepList.Children)
builder.AddStep(entry.MakeStep());
onRecipeCreated(builder.Build());
}
}

@ -4,7 +4,8 @@
x:Class="ShoopNCook.Pages.FavoritesPage"
Title="FavoritesPage"
xmlns:views="clr-namespace:ShoopNCook.Views"
BackgroundColor="{StaticResource BackgroundPrimary}">
BackgroundColor="{StaticResource BackgroundPrimary}"
NavigatedTo="ContentPage_NavigatedTo">
<Grid
RowDefinitions="Auto, *">
@ -15,7 +16,6 @@
ColumnDefinitions="*"
MaximumHeightRequest="60">
<Label
Grid.Column="0"
FontSize="24"
@ -35,18 +35,9 @@
AlignItems="Start"
AlignContent="Start"
Direction="Row"
Wrap="Wrap">
Wrap="Wrap"
x:Name="RecipeViewLayout">
<views:RecipeView Margin="5" Note="4.5" Title="Spaghetti Bolognese" Subtitle="30 min"/>
<views:RecipeView Margin="5" Note="3" Title="Chickend Curry" Subtitle="45 min"/>
<views:RecipeView Margin="5" Note="0.2" Title="Beef Stroganoff" Subtitle="10 min"/>
<views:RecipeView Margin="5" Note="1.6" Title="Fish And Ships" Subtitle="15 min"/>
<views:RecipeView Margin="5" Note="5" Title="Caesar Salad" Subtitle="20 min"/>
<views:RecipeView Margin="5" Note="3.5" Title="Vegetables" Subtitle="60 min"/>
<views:RecipeView Margin="5" Note="4.6" Title="Guacamole" Subtitle="90 min"/>
<views:RecipeView Margin="5" Note="4" Title="Pad Thai" Subtitle="10 min"/>
<views:RecipeView Margin="5" Note="3" Title="French Toast" Subtitle="5 min"/>
<views:RecipeView Margin="5" Note="2" Title="Margherita Pizza" Subtitle="2 min"/>
</FlexLayout>
</ScrollView>
</Grid>

@ -2,11 +2,45 @@ using Models;
namespace ShoopNCook.Pages;
using Endpoint;
using LocalEndpoint;
using Models;
using ShoopNCook.Views;
using System.Security.Principal;
public partial class FavoritesPage : ContentPage
{
public FavoritesPage(Account account, IApp app)
private readonly Account account;
private readonly IUserNotifier notifier;
private IRecipesService service;
public FavoritesPage(Account account, IUserNotifier notifier, IRecipesService service)
{
InitializeComponent();
this.account = account;
this.notifier = notifier;
this.service = service;
UpdateFavorites();
}
private void UpdateFavorites()
{
IAccountRecipesPreferences preferences = service.GetPreferencesOf(account);
RecipeViewLayout.Children.Clear();
preferences.GetFavorites().ForEach(info =>
{
RecipeViewLayout.Children.Add(new RecipeView(info, () =>
{
Recipe recipe = service.GetRecipe(info);
Shell.Current.Navigation.PushAsync(new RecipePage(recipe, notifier, preferences, 1));
}));
});
}
private void ContentPage_NavigatedTo(object sender, NavigatedToEventArgs e)
{
UpdateFavorites();
}
}

@ -62,7 +62,7 @@
Margin="20"
HeightRequest="30">
<Label
Text="Popular recipe"
Text="Popular recipes"
Style="{StaticResource h2}"/>
<Label
@ -75,12 +75,9 @@
Orientation="Horizontal">
<HorizontalStackLayout
Spacing="10"
Padding="0,0,0,40">
<views:RecipeView/>
<views:RecipeView/>
<views:RecipeView/>
<views:RecipeView/>
<views:RecipeView/>
Padding="0,0,0,40"
x:Name="PopularsList">
</HorizontalStackLayout>
</ScrollView>
@ -109,14 +106,9 @@
AlignItems="Start"
AlignContent="Start"
Direction="Row"
Wrap="Wrap">
<views:RecipeView Margin="2.5"/>
<views:RecipeView Margin="2.5"/>
<views:RecipeView Margin="2.5"/>
<views:RecipeView Margin="2.5"/>
<views:RecipeView Margin="2.5"/>
<views:RecipeView Margin="2.5"/>
<views:RecipeView Margin="2.5"/>
Wrap="Wrap"
x:Name="RecommendedList">
</FlexLayout>
</ScrollView>
</VerticalStackLayout>

@ -1,17 +1,42 @@
using Models;
namespace ShoopNCook.Pages;
using Models;
using ShoopNCook.Views;
using Endpoint;
using LocalEndpoint;
public partial class HomePage : ContentPage
{
public HomePage(Account account, IApp app)
public HomePage(Account account, IUserNotifier notifier, IEndpoint endpoint)
{
InitializeComponent();
IRecipesService service = endpoint.RecipesService;
IAccountRecipesPreferences preferences = service.GetPreferencesOf(account);
//TODO this code can be factorised
void PushRecipe(Layout layout, RecipeInfo info)
{
layout.Children.Add(new RecipeView(info, () =>
{
Recipe recipe = service.GetRecipe(info);
Shell.Current.Navigation.PushAsync(new RecipePage(recipe, notifier, preferences, 1));
}));
}
service.PopularRecipes().ForEach(recipe => PushRecipe(PopularsList, recipe));
preferences.GetRecommendedRecipes().ForEach(recipe => PushRecipe(RecommendedList, recipe));
ProfilePictureImage.Source = ImageSource.FromUri(account.User.ProfilePicture);
ProfilePictureName.Text = account.User.Name;
}
private async void OnSyncButtonClicked(object sender, EventArgs e)
private void OnSyncButtonClicked(object sender, EventArgs e)
{
await Shell.Current.Navigation.PushAsync(new SearchPage());
Shell.Current.Navigation.PushAsync(new SearchPage());
}
}

@ -16,14 +16,14 @@ public partial class MorePage : ContentPage
this.controller = controller;
}
private async void OnMyRecipesButtonTapped(object sender, EventArgs e)
private void OnMyRecipesButtonTapped(object sender, EventArgs e)
{
await Shell.Current.Navigation.PushAsync(new MyRecipesPage());
controller.GoToMyRecipesPage();
}
private async void OnEditProfileButtonTapped(object sender, EventArgs e)
private void OnEditProfileButtonTapped(object sender, EventArgs e)
{
await Shell.Current.Navigation.PushAsync(new ProfilePage());
controller.GoToProfilePage();
}
private void OnLogoutButtonTapped(object sender, EventArgs e)

@ -4,7 +4,8 @@
x:Class="ShoopNCook.Pages.MyListPage"
Title="MyList"
BackgroundColor="{StaticResource BackgroundPrimary}"
xmlns:views="clr-namespace:ShoopNCook.Views">
xmlns:views="clr-namespace:ShoopNCook.Views"
NavigatedTo="ContentPage_NavigatedTo">
<Grid
RowDefinitions="Auto, *, Auto">
<!-- Header label and return button -->
@ -32,23 +33,13 @@
VerticalOptions="Center"/>
</Grid>
<!-- Favorite items -->
<!-- Account Recipe List items -->
<ScrollView
Grid.Row="1">
<VerticalStackLayout
Padding="30, 0, 30, 0"
Spacing="12">
<views:StoredRecipeView Note="4.5" Title="Spaghetti Bolognese"/>
<views:StoredRecipeView Note="3" Title="Chickend Curry"/>
<views:StoredRecipeView Note="0.2" Title="Beef Stroganoff"/>
<views:StoredRecipeView Note="1.6" Title="Fish And Ships" />
<views:StoredRecipeView Note="5" Title="Caesar Salad"/>
<views:StoredRecipeView Note="3.5" Title="Vegetables"/>
<views:StoredRecipeView Note="4.6" Title="Guacamole"/>
<views:StoredRecipeView Note="4" Title="Pad Thai"/>
<views:StoredRecipeView Note="3" Title="French Toast"/>
<views:StoredRecipeView Note="2" Title="Margherita Pizza"/>
Spacing="12"
x:Name="RecipesLayout">
</VerticalStackLayout>
</ScrollView>

@ -1,11 +1,44 @@
using Endpoint;
using LocalEndpoint;
using Models;
using ShoopNCook.Views;
namespace ShoopNCook.Pages;
public partial class MyListPage : ContentPage
{
public MyListPage(Account account, IApp app)
private readonly IAccountRecipesPreferences preferences;
private readonly IUserNotifier notifier;
private readonly IRecipesService service;
public MyListPage(Account account, IUserNotifier notifier, IRecipesService service)
{
InitializeComponent();
this.preferences = service.GetPreferencesOf(account);
this.notifier = notifier;
this.service = service;
UpdateMyList();
}
private void UpdateMyList()
{
RecipesLayout.Children.Clear();
preferences.GetWeeklyList().ForEach(tuple =>
{
RecipeInfo info = tuple.Item1;
RecipesLayout.Children.Add(new StoredRecipeView(info, tuple.Item2, amount =>
{
Recipe recipe = service.GetRecipe(info);
Shell.Current.Navigation.PushAsync(new RecipePage(recipe, notifier, preferences, amount));
}));
});
}
private void ContentPage_NavigatedTo(object sender, NavigatedToEventArgs e)
{
UpdateMyList();
}
}

@ -38,18 +38,9 @@
AlignItems="Start"
AlignContent="Start"
Direction="Row"
Wrap="Wrap">
Wrap="Wrap"
x:Name="RecipesLayout">
<views:OwnedRecipeView Margin="5" Note="4.5" Title="Spaghetti Bolognese"/>
<views:OwnedRecipeView Margin="5" Note="3" Title="Chickend Curry"/>
<views:OwnedRecipeView Margin="5" Note="0.2" Title="Beef Stroganoff"/>
<views:OwnedRecipeView Margin="5" Note="1.6" Title="Fish And Ships"/>
<views:OwnedRecipeView Margin="5" Note="5" Title="Caesar Salad"/>
<views:OwnedRecipeView Margin="5" Note="3.5" Title="Vegetables"/>
<views:OwnedRecipeView Margin="5" Note="4.6" Title="Guacamole"/>
<views:OwnedRecipeView Margin="5" Note="4" Title="Pad Thai"/>
<views:OwnedRecipeView Margin="5" Note="3" Title="French Toast"/>
<views:OwnedRecipeView Margin="5" Note="2" Title="Margherita Pizza"/>
</FlexLayout>
</ScrollView>
@ -61,7 +52,7 @@
Style="{StaticResource UserButton}"
BackgroundColor="{StaticResource Selected}"
Text="Add a new recipe"
Clicked="AddRecipeButtonClicked"/>
Clicked="OnAddRecipeButtonClicked"/>
</Border>
</Grid>
</ContentPage>

@ -1,19 +1,85 @@
using Endpoint;
using LocalEndpoint;
using Models;
using ShoopNCook.Views;
namespace ShoopNCook.Pages;
public partial class MyRecipesPage : ContentPage
{
public MyRecipesPage()
private IUserNotifier notifier;
private IRecipesService service;
private Account account;
public MyRecipesPage(
Account account,
IRecipesService service,
IUserNotifier notifier)
{
InitializeComponent();
this.notifier = notifier;
this.service = service;
this.account = account;
service
.GetRecipesOf(account)
.GetAccountRecipes()
.ForEach(AddRecipeView);
}
private void AddRecipeView(RecipeInfo info)
{
RecipesLayout.Children.Add(new OwnedRecipeView(info, () =>
{
Recipe recipe = service.GetRecipe(info);
IAccountRecipesPreferences preferences = service.GetPreferencesOf(account);
Shell.Current.Navigation.PushAsync(new RecipePage(recipe, notifier, preferences, 1));
},
() => RemoveRecipe(info)
));
}
private void RemoveRecipe(RecipeInfo info)
{
IAccountOwnedRecipes recipes = service.GetRecipesOf(account);
if (!recipes.RemoveRecipe(info))
{
notifier.Error("Could not remove recipe");
return;
}
private async void OnBackButtonClicked(object sender, EventArgs e)
foreach (OwnedRecipeView view in RecipesLayout.Children)
{
if (view.IsViewing(info))
{
await Navigation.PopAsync();
RecipesLayout.Remove(view);
break;
}
private async void AddRecipeButtonClicked(object sender, EventArgs e)
}
notifier.Success("Recipe successfully removed");
}
private void OnBackButtonClicked(object sender, EventArgs e)
{
await Shell.Current.Navigation.PushAsync(new CreateRecipePage());
Navigation.PopAsync();
}
private void OnAddRecipeButtonClicked(object sender, EventArgs e)
{
IAccountOwnedRecipes recipes = service.GetRecipesOf(account);
var page = new CreateRecipePage(account.User, notifier, recipe =>
{
if (!recipes.UploadRecipe(recipe))
{
notifier.Error("Could not upload recipe.");
return;
}
notifier.Success("Recipe Successfuly uploaded !");
AddRecipeView(recipe.Info);
Shell.Current.Navigation.PopAsync(); //go back to current recipe page.
});
Shell.Current.Navigation.PushAsync(page); //display RecipePage editor
}
}

@ -1,8 +1,10 @@
using Models;
namespace ShoopNCook.Pages;
public partial class ProfilePage : ContentPage
{
public ProfilePage()
public ProfilePage(Account account)
{
InitializeComponent();
}

@ -8,7 +8,6 @@
BackgroundColor="{StaticResource BackgroundPrimary}">
<Grid
RowDefinitions="90*, 10*"
Padding="10">
@ -51,7 +50,8 @@
StrokeShape="RoundRectangle 20"
BackgroundColor="{StaticResource ImageBackground}">
<Image
HeightRequest="250"/>
HeightRequest="250"
x:Name="RecipeImage"/>
</Border>
<!--Steps-->
@ -146,7 +146,8 @@
BackgroundColor="{StaticResource Selected}"
VerticalOptions="Center"
HorizontalOptions="Center"
Text="Submit"/>
Text="Submit"
Clicked="OnSubmitReviewClicked"/>
</Border>
</HorizontalStackLayout>
</Grid>
@ -170,7 +171,8 @@
Text="Add to list"
Style="{StaticResource UserButton}"
TextColor="White"
BackgroundColor="Gray">
BackgroundColor="Gray"
Clicked="OnAddToMyListClicked">
</Button>
</FlexLayout>
</Grid>

@ -1,5 +1,8 @@
using ShoopNCook.Views;
using System.Windows.Input;
using Models;
using LocalEndpoint;
using Endpoint;
namespace ShoopNCook.Pages;
@ -9,53 +12,51 @@ public partial class RecipePage : ContentPage
private uint note;
private bool isFavorite;
public ICommand StarCommand => new Command<string>(count =>
{
SetNote(uint.Parse(count));
});
public RecipePage() :
this("Recipe Sample", 32, 250,
true, 2, 0,
new List<IngredientView> {
new IngredientView("Chocolate", 25, "g"),
new IngredientView("Flour", 250, "g"),
new IngredientView("Sugar", 0.5F, "kg")
},
new List<string> { "This is the first preparation step", "add to furnace and wait", "Enjoy !" }
)
{}
public RecipePage(
string name,
uint cookTime,
uint energy,
bool isFavorite,
uint nbPers,
uint note,
List<IngredientView> ingredients,
List<string> steps
)
private IAccountRecipesPreferences preferences;
private IUserNotifier notifier;
private RecipeInfo info;
public ICommand StarCommand => new Command<string>(count => SetNote(uint.Parse(count)));
public RecipePage(Recipe recipe, IUserNotifier notifier, IAccountRecipesPreferences preferences, uint amount)
{
InitializeComponent();
Counter.Count = nbPers;
this.preferences = preferences;
this.notifier = notifier;
this.info = recipe.Info;
AccountRecipeRate rate = preferences.GetRate(recipe.Info);
note = rate.Rate;
isFavorite = rate.IsFavorite;
SetFavorite(isFavorite);
SetNote(note);
CookTime.Text = cookTime.ToString();
Energy.Text = energy.ToString();
RecipeName.Text = name;
RecipeInfo info = recipe.Info;
Counter.Count = amount;
CookTime.Text = info.CookTimeMins.ToString();
Energy.Text = info.CalPerPers.ToString() + " cal/pers";
RecipeName.Text = info.Name;
if (info.Image != null)
RecipeImage.Source = ImageSource.FromUri(info.Image);
foreach (IngredientView iv in ingredients)
IngredientList.Add(iv);
foreach (Ingredient ingredient in recipe.Ingredients)
IngredientList.Add(new IngredientView(ingredient));
//retrieves the app's styles
var styles = Application.Current.Resources.MergedDictionaries.ElementAt(1);
int count = 0;
foreach (string step in steps) {
foreach (PreparationStep step in recipe.Steps) {
//TODO display name of PreparationSteps.
Label label = new Label();
label.Style = (Style)styles["Small"];
label.Text = "Step " + ++count + ": " + step;
label.Text = "Step " + ++count + ": " + step.Description;
StepList.Add(label);
}
}
@ -72,32 +73,47 @@ public partial class RecipePage : ContentPage
i++;
}
else
{
img.Source = ImageSource.FromFile("star_empty.svg");
}
}
}
private void OnFavorite(object o, EventArgs e)
{
SetFavorite(!isFavorite);
}
private void OnSubmitReviewClicked(object o, EventArgs e)
{
preferences.SetReviewScore(info, note);
notifier.Success("Your review has been successfuly submited");
}
private void OnAddToMyListClicked(object o, EventArgs e)
{
if (!preferences.AddToWeeklyList(info, Counter.Count))
notifier.Notice("You already added this recipe to you weekly list!");
else
notifier.Success("Recipe added to your weekly list.");
}
private void SetFavorite(bool isFavorite)
{
this.isFavorite = isFavorite;
if (isFavorite)
{
Favorite.Source = ImageSource.FromFile("hearth_on.svg");
preferences.AddToFavorites(info);
}
else
{
Favorite.Source = ImageSource.FromFile("hearth_off.svg");
preferences.RemoveFromFavorites(info);
}
}
private async void OnBackButtonClicked(object sender, EventArgs e)
private void OnBackButtonClicked(object sender, EventArgs e)
{
await Navigation.PopAsync();
Navigation.PopAsync();
}
}

@ -105,14 +105,9 @@
AlignItems="Start"
AlignContent="Start"
Direction="Row"
Wrap="Wrap">
<views:RecipeView Margin="5"/>
<views:RecipeView Margin="5"/>
<views:RecipeView Margin="5"/>
<views:RecipeView Margin="5"/>
<views:RecipeView Margin="5"/>
<views:RecipeView Margin="5"/>
<views:RecipeView Margin="5"/>
Wrap="Wrap"
x:Name="ResultSearchView">
</FlexLayout>
</ScrollView>

@ -1,10 +1,12 @@
namespace ShoopNCook.Pages;
using Models;
using ShoopNCook.Views;
public partial class SearchPage : ContentPage
{
public SearchPage()
{
InitializeComponent();
//TODO
}
private async void OnBackButtonClicked(object sender, EventArgs e)
{

Loading…
Cancel
Save