diff --git a/App.xaml.cs b/App.xaml.cs
index 9c150df..c873ce2 100644
--- a/App.xaml.cs
+++ b/App.xaml.cs
@@ -25,7 +25,7 @@ public partial class App : Application, ConnectionObserver, IApp
public void ForceLogin()
{
- Shell shell = new ConnectAppShell(this, Endpoint.AccountManager, Notifier);
+ Shell shell = new ConnectAppShell(this, Endpoint.AuthService, Notifier);
shell.GoToAsync("//Splash");
MainPage = shell;
}
diff --git a/ConnectAppShell.xaml.cs b/ConnectAppShell.xaml.cs
index 29f3275..dd44352 100644
--- a/ConnectAppShell.xaml.cs
+++ b/ConnectAppShell.xaml.cs
@@ -7,7 +7,7 @@ using ShoopNCook.Pages;
public partial class ConnectAppShell : Shell
{
- public ConnectAppShell(ConnectionObserver observer, IAccountManager accounts, IUserNotifier notifier)
+ public ConnectAppShell(ConnectionObserver observer, IAuthService accounts, IUserNotifier notifier)
{
ConnectionController controller = new ConnectionController(observer, accounts, notifier);
InitializeComponent();
diff --git a/Controllers/ConnectionController.cs b/Controllers/ConnectionController.cs
index 6ecfd51..0e59bb7 100644
--- a/Controllers/ConnectionController.cs
+++ b/Controllers/ConnectionController.cs
@@ -6,9 +6,9 @@ namespace ShoopNCook.Controllers
public class ConnectionController : LoginController, RegisterController
{
private readonly ConnectionObserver observer;
- private readonly IAccountManager accounts;
+ private readonly IAuthService accounts;
private readonly IUserNotifier notifier;
- public ConnectionController(ConnectionObserver observer, IAccountManager accounts, IUserNotifier notifier) {
+ public ConnectionController(ConnectionObserver observer, IAuthService accounts, IUserNotifier notifier) {
this.observer = observer;
this.accounts = accounts;
this.notifier = notifier;
diff --git a/LocalEndpoint/AccountData.cs b/LocalEndpoint/AccountData.cs
deleted file mode 100644
index cf84b07..0000000
--- a/LocalEndpoint/AccountData.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-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);
-}
diff --git a/LocalEndpoint/Constants.cs b/LocalEndpoint/Constants.cs
deleted file mode 100644
index 249a016..0000000
--- a/LocalEndpoint/Constants.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-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");
-
- }
-}
diff --git a/LocalEndpoint/LocalEndpoint.cs b/LocalEndpoint/LocalEndpoint.cs
deleted file mode 100644
index 9b27f1e..0000000
--- a/LocalEndpoint/LocalEndpoint.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using Endpoint;
-using Models;
-using System.Collections.Immutable;
-
-namespace LocalEndpoint
-{
-
- ///
- /// The local endpoint is an implementation of the Endpoint API definition.
- ///
- ///
- public class LocalEndpoint : IEndpoint
- {
- 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 { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List { 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 { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List { 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 { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List { 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 { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List { 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 { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List { new PreparationStep("Step 1", "Bake the eggs") }.ToImmutableList()));
-
- recipesService = new RecipesService(db);
- }
-
- public IAccountManager AccountManager => accountManager;
-
- public IRecipesService RecipesService => recipesService;
-
- }
-}
\ No newline at end of file
diff --git a/LocalEndpoint/RecipesDatabase.cs b/LocalEndpoint/RecipesDatabase.cs
deleted file mode 100644
index 511cd80..0000000
--- a/LocalEndpoint/RecipesDatabase.cs
+++ /dev/null
@@ -1,43 +0,0 @@
-using Models;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-
-namespace LocalEndpoint
-{
-
- //Simple class to simulate a recipe database
- internal class RecipesDatabase
- {
-
- private Dictionary recipes = new Dictionary();
-
- 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 ListAll()
- {
- return recipes.Values.ToImmutableList();
- }
-
- }
-}
diff --git a/LocalEndpoint/AccountOwnedRecipes.cs b/LocalServices/AccountOwnedRecipes.cs
similarity index 73%
rename from LocalEndpoint/AccountOwnedRecipes.cs
rename to LocalServices/AccountOwnedRecipes.cs
index e3d8059..633d96e 100644
--- a/LocalEndpoint/AccountOwnedRecipes.cs
+++ b/LocalServices/AccountOwnedRecipes.cs
@@ -1,4 +1,5 @@
using LocalEndpoint;
+using LocalEndpoint.Data;
using Models;
using System.Collections.Immutable;
@@ -10,17 +11,18 @@ namespace Endpoint
public Account Account { get; init; }
private readonly Dictionary ownedRecipes = new Dictionary();
- private readonly RecipesDatabase db;
+ private readonly Database db;
- public AccountOwnedRecipes(Account account, RecipesDatabase db)
+ public AccountOwnedRecipes(Account account, Database db)
{
Account = account;
this.db = db;
//Retrieve all owned recipes from database.
- db.ListAll().ForEach(recipe =>
+ db.ListAllRecipes().ForEach(recipe =>
{
- if (recipe.Owner == account.User) ownedRecipes[recipe.Info.Id] = recipe;
+ if (recipe.Owner.Equals(account.User))
+ ownedRecipes.Add(recipe.Info.Id, recipe);
});
}
@@ -31,14 +33,14 @@ namespace Endpoint
{
return false;
}
- db.Insert(recipe);
+ db.InsertRecipe(recipe);
ownedRecipes.Add(id, recipe);
return true;
}
public bool RemoveRecipe(RecipeInfo info)
{
- db.Remove(info.Id);
+ db.RemoveRecipe(info.Id);
return ownedRecipes.Remove(info.Id);
}
diff --git a/LocalEndpoint/AccountRecipesPreferences.cs b/LocalServices/AccountRecipesPreferences.cs
similarity index 50%
rename from LocalEndpoint/AccountRecipesPreferences.cs
rename to LocalServices/AccountRecipesPreferences.cs
index ce8f296..a7d848a 100644
--- a/LocalEndpoint/AccountRecipesPreferences.cs
+++ b/LocalServices/AccountRecipesPreferences.cs
@@ -1,24 +1,15 @@
using Endpoint;
+using LocalEndpoint.Data;
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 ratings = new Dictionary();
- //Binds a recipe's id to its amount of person stored in the account's weekly list
- private readonly Dictionary weekly = new Dictionary();
-
- private readonly RecipesDatabase db;
- public AccountRecipesPreferences(Account account, RecipesDatabase db)
+ private readonly Database db;
+ public AccountRecipesPreferences(Account account, Database db)
{
Account = account;
this.db = db;
@@ -28,15 +19,17 @@ namespace LocalEndpoint
public ImmutableList GetRecommendedRecipes()
{
- return db.ListAll().ConvertAll(recipe => recipe.Info);
+ return db.ListAllRecipes().ConvertAll(recipe => recipe.Info);
}
public ImmutableList GetFavorites()
{
List favorites = new List();
- foreach (Recipe recipe in db.ListAll())
+ var ratings = db.ListRatesOf(Account.User.Id);
+
+ foreach (Recipe recipe in db.ListAllRecipes())
{
- if (ratings.TryGetValue(recipe.Info.Id, out AccountRecipeRate? rate))
+ if (ratings.TryGetValue(recipe.Info.Id, out RecipeRate? rate))
{
if (rate.IsFavorite)
favorites.Add(recipe.Info);
@@ -47,10 +40,12 @@ namespace LocalEndpoint
public ImmutableList<(RecipeInfo, uint)> GetWeeklyList()
{
+ var weeklyDict = db.GetRecipeListOf(Account.User.Id);
List<(RecipeInfo, uint)> weekly = new List<(RecipeInfo, uint)>();
- foreach (Recipe recipe in db.ListAll())
+
+ foreach (Recipe recipe in db.ListAllRecipes())
{
- if (this.weekly.TryGetValue(recipe.Info.Id, out uint personAmmount))
+ if (weeklyDict.TryGetValue(recipe.Info.Id, out uint personAmmount))
{
weekly.Add((recipe.Info, personAmmount));
}
@@ -59,44 +54,56 @@ namespace LocalEndpoint
}
- public AccountRecipeRate GetRate(RecipeInfo info)
+ public RecipeRate GetRate(RecipeInfo info)
{
- AccountRecipeRate rate = null;
+ RecipeRate rate = null;
+ var ratings = db.ListRatesOf(Account.User.Id);
+
if (!ratings.TryGetValue(info.Id, out rate))
{
- rate = new AccountRecipeRate();
+ rate = new RecipeRate();
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))
+ var weeklyDict = db.GetRecipeListOf(Account.User.Id);
+ if (weeklyDict.ContainsKey(info.Id))
return false;
- weekly[info.Id] = persAmount;
+ db.InsertInUserList(Account.User.Id, info.Id, persAmount);
return true;
}
+
+ public void AddToFavorites(RecipeInfo info)
+ {
+ Guid userId = Account.User.Id;
+ var ratings = db.ListRatesOf(userId);
+ RecipeRate rate = GetRate(info);
+
+ db.InsertRate(userId, info.Id, new RecipeRate(true, rate.Rate));
+ }
+
public void RemoveFromFavorites(RecipeInfo info)
{
- AccountRecipeRate rate = GetRate(info);
- ratings[info.Id] = new AccountRecipeRate(false, rate.Rate);
+ Guid userId = Account.User.Id;
+ var ratings = db.ListRatesOf(userId);
+ RecipeRate rate = GetRate(info);
+
+ db.InsertRate(userId, info.Id, new RecipeRate(false, rate.Rate));
}
public void SetReviewScore(RecipeInfo info, uint score)
{
- AccountRecipeRate rate = GetRate(info);
- ratings[info.Id] = new AccountRecipeRate(rate.IsFavorite, score);
+ Guid userId = Account.User.Id;
+ var ratings = db.ListRatesOf(userId);
+ RecipeRate rate = GetRate(info);
+
+ db.InsertRate(userId, info.Id, new RecipeRate(rate.IsFavorite, score));
}
}
}
diff --git a/LocalServices/AccountServices.cs b/LocalServices/AccountServices.cs
new file mode 100644
index 0000000..02d4d6a
--- /dev/null
+++ b/LocalServices/AccountServices.cs
@@ -0,0 +1,6 @@
+using Endpoint;
+
+namespace LocalEndpoint
+{
+ internal record AccountServices(IAccountOwnedRecipes Recipes, IAccountRecipesPreferences Preferences);
+}
diff --git a/LocalEndpoint/ConnectionManager.cs b/LocalServices/AuthService.cs
similarity index 50%
rename from LocalEndpoint/ConnectionManager.cs
rename to LocalServices/AuthService.cs
index 427814e..47ca4fb 100644
--- a/LocalEndpoint/ConnectionManager.cs
+++ b/LocalServices/AuthService.cs
@@ -5,28 +5,33 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using LocalEndpoint.Data;
+using System.Security.Cryptography;
namespace LocalEndpoint
{
- internal class ConnectionManager : IAccountManager
+ internal class AuthService : IAuthService
{
- private Account userAccount = Constants.MAIN_USER_ACCOUNT;
- private string userPassword = Constants.MAIN_USER_PASSWORD;
- public Account? Login(string email, string password)
+ private readonly Database db;
+
+
+ public AuthService(Database db)
{
- if (Constants.MAIN_USER_ACCOUNT.Email == email && Constants.MAIN_USER_PASSWORD == password)
- return Constants.MAIN_USER_ACCOUNT;
- return null;
+ this.db = db;
}
+ public Account? Login(string email, string password)
+ {
+ return db.GetAccount(email, password);
+ }
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;
+ var userAccount = new Account(new User(Constants.DEFAULT_ACCOUNT_IMAGE, username, Guid.NewGuid()), email);
+ db.InsertAccount(userAccount, password);
return userAccount;
}
}
diff --git a/LocalServices/Constants.cs b/LocalServices/Constants.cs
new file mode 100644
index 0000000..75f80d1
--- /dev/null
+++ b/LocalServices/Constants.cs
@@ -0,0 +1,9 @@
+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");
+ }
+}
diff --git a/LocalServices/Data/AccountData.cs b/LocalServices/Data/AccountData.cs
new file mode 100644
index 0000000..332c7e9
--- /dev/null
+++ b/LocalServices/Data/AccountData.cs
@@ -0,0 +1,17 @@
+using Models;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LocalEndpoint.Data
+{
+ [DataContract]
+ internal record AccountData(
+ [property: DataMember] Guid UserId,
+ [property: DataMember] string Email,
+ [property: DataMember] string PasswordHash
+ );
+}
diff --git a/LocalServices/Data/CatastrophicPerformancesDatabase.cs b/LocalServices/Data/CatastrophicPerformancesDatabase.cs
new file mode 100644
index 0000000..32c48c1
--- /dev/null
+++ b/LocalServices/Data/CatastrophicPerformancesDatabase.cs
@@ -0,0 +1,160 @@
+using Models;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace LocalEndpoint.Data
+{
+ ///
+ /// Database implementation with catastrophic performances.
+ /// This database implementation persists data in xml and will save all the data in their files on each mutable requests.
+ ///
+ internal class CatastrophicPerformancesDatabase : Database
+ {
+
+ private static readonly DataContractSerializer RECIPES_SERIALIZER = new DataContractSerializer(typeof(Dictionary));
+ private static readonly DataContractSerializer USERS_SERIALIZER = new DataContractSerializer(typeof(Dictionary));
+ private static readonly DataContractSerializer ACCOUNTS_SERIALIZER = new DataContractSerializer(typeof(Dictionary));
+
+ private static readonly string RECIPES_FILENAME = "recipes_data.xml";
+ private static readonly string USERS_FILENAME = "users_data.xml";
+ private static readonly string ACCOUNTS_FILENAME = "accounts_data.xml";
+
+ private readonly Dictionary recipesData;
+ private readonly Dictionary usersData;
+ private readonly Dictionary accountsData;
+
+ private readonly string dbPath;
+
+
+ public CatastrophicPerformancesDatabase(string folderPath)
+ {
+ dbPath = folderPath;
+ if (!Directory.Exists(folderPath))
+ Directory.CreateDirectory(folderPath);
+
+ usersData = Load(USERS_FILENAME, USERS_SERIALIZER);
+ recipesData = Load(RECIPES_FILENAME, RECIPES_SERIALIZER);
+ accountsData = Load(ACCOUNTS_FILENAME, ACCOUNTS_SERIALIZER);
+ }
+
+ public bool IsEmpty()
+ {
+ return recipesData.Count == 0 && usersData.Count == 0 && accountsData.Count == 0;
+ }
+
+ public Account? GetAccount(string email, string passwordHash)
+ {
+ if (!accountsData.TryGetValue(email, out AccountData? data))
+ return null;
+
+ if (data.PasswordHash != passwordHash) return null;
+ return new Account(usersData[data.UserId].User, data.Email);
+ }
+
+ public void InsertAccount(Account account, string passwordHash)
+ {
+ accountsData[account.Email] = new AccountData(account.User.Id, account.Email, passwordHash);
+ Save(ACCOUNTS_FILENAME, ACCOUNTS_SERIALIZER, accountsData);
+ }
+
+ public Recipe GetRecipe(Guid id)
+ {
+ return ConvertRecipeDataToRecipe(recipesData[id]);
+ }
+
+ public RecipeRate GetRecipeRate(Guid user, Guid recipe)
+ {
+ return usersData[user].Rates[recipe];
+ }
+
+ public void InsertInUserList(Guid userId, Guid recipeId, uint persAmount)
+ {
+ usersData[userId].RecipesList[recipeId] = persAmount;
+ Save(USERS_FILENAME, USERS_SERIALIZER, usersData);
+ }
+
+ public void RemoveFromUserList(Guid userId, Guid recipeId)
+ {
+ usersData[userId].RecipesList.Remove(recipeId);
+ Save(USERS_FILENAME, USERS_SERIALIZER, usersData);
+ }
+
+
+ public void InsertRecipe(Recipe recipe)
+ {
+ recipesData[recipe.Info.Id] = new RecipeData(recipe.Info, recipe.Owner.Id, recipe.Ingredients, recipe.Steps);
+ Save(RECIPES_FILENAME, RECIPES_SERIALIZER, recipesData);
+ }
+
+ public void InsertUser(User user)
+ {
+ usersData[user.Id] = new UserData(user, new Dictionary(), new Dictionary());
+ Save(USERS_FILENAME, USERS_SERIALIZER, usersData);
+ }
+
+ public void InsertRate(Guid userId, Guid recipeId, RecipeRate rate)
+ {
+ usersData[userId].Rates[recipeId] = rate;
+ Save(USERS_FILENAME, USERS_SERIALIZER, usersData);
+ }
+
+ public void RemoveRecipe(Guid id)
+ {
+ recipesData.Remove(id);
+ Save(RECIPES_FILENAME, RECIPES_SERIALIZER, recipesData);
+ }
+
+ public ImmutableList ListAllRecipes()
+ {
+ return recipesData.Values.ToImmutableList().ConvertAll(ConvertRecipeDataToRecipe);
+ }
+
+
+ public ImmutableDictionary ListRatesOf(Guid user)
+ {
+ return usersData[user].Rates.ToImmutableDictionary();
+ }
+
+ public ImmutableDictionary GetRecipeListOf(Guid user)
+ {
+ return usersData[user].RecipesList.ToImmutableDictionary();
+ }
+
+ private Recipe ConvertRecipeDataToRecipe(RecipeData rd)
+ {
+ var owner = usersData[rd.OwnerID].User;
+ return new Recipe(rd.Info, owner, rd.Ingredients, rd.Steps);
+ }
+
+ private Dictionary Load(string fileName, DataContractSerializer deserializer)
+ {
+ var file = dbPath + "/" + fileName;
+ var fileInfo = new FileInfo(file);
+
+ if (!fileInfo.Exists)
+ fileInfo.Create();
+
+ if (fileInfo.Length == 0)
+ return new Dictionary(); //file is empty thus there is nothing to deserialize
+ Console.WriteLine(File.ReadAllText(file));
+
+ using (var stream = File.OpenRead(file))
+ return deserializer.ReadObject(stream) as Dictionary ?? throw new Exception("object read from " + file + " is not a dictionnary");
+ }
+
+ private void Save(string fileName, DataContractSerializer serializer, Dictionary dict)
+ {
+ using (var stream = File.OpenWrite(dbPath + "/" + fileName))
+ {
+ serializer.WriteObject(stream, dict);
+ stream.Flush();
+ }
+ }
+
+ }
+}
diff --git a/LocalServices/Data/Database.cs b/LocalServices/Data/Database.cs
new file mode 100644
index 0000000..130093a
--- /dev/null
+++ b/LocalServices/Data/Database.cs
@@ -0,0 +1,36 @@
+
+using Models;
+using System.Collections.Immutable;
+
+namespace LocalEndpoint.Data
+{
+
+ // The database interface defines all the different kinds of requests the LocalEndpoint needs to store and retrieve data.
+ public interface Database
+ {
+
+ public Recipe GetRecipe(Guid id);
+
+ public RecipeRate GetRecipeRate(Guid user, Guid recipe);
+
+ public Account? GetAccount(string email, string passwordHash);
+
+ public void InsertInUserList(Guid userId, Guid recipeId, uint persAmount);
+ public void RemoveFromUserList(Guid userId, Guid recipeId);
+ public void InsertAccount(Account account, string passwordHash);
+
+ public void InsertRecipe(Recipe recipe);
+
+ public void InsertUser(User user);
+
+ public void InsertRate(Guid userId, Guid recipeId, RecipeRate rate);
+
+ public void RemoveRecipe(Guid id);
+
+ public ImmutableList ListAllRecipes();
+
+ public ImmutableDictionary ListRatesOf(Guid user);
+
+ public ImmutableDictionary GetRecipeListOf(Guid user);
+ }
+}
diff --git a/LocalServices/Data/RecipeData.cs b/LocalServices/Data/RecipeData.cs
new file mode 100644
index 0000000..431f9b6
--- /dev/null
+++ b/LocalServices/Data/RecipeData.cs
@@ -0,0 +1,14 @@
+using Models;
+using System.Collections.Immutable;
+using System.Runtime.Serialization;
+
+namespace LocalEndpoint.Data
+{
+ [DataContract]
+ internal record RecipeData(
+ [property: DataMember] RecipeInfo Info,
+ [property: DataMember] Guid OwnerID,
+ [property: DataMember] ImmutableList Ingredients,
+ [property: DataMember] ImmutableList Steps
+ );
+}
\ No newline at end of file
diff --git a/LocalServices/Data/UserData.cs b/LocalServices/Data/UserData.cs
new file mode 100644
index 0000000..847d659
--- /dev/null
+++ b/LocalServices/Data/UserData.cs
@@ -0,0 +1,14 @@
+
+
+using Models;
+using System.Runtime.Serialization;
+
+namespace LocalEndpoint.Data
+{
+ [DataContract]
+ internal record UserData(
+ [property: DataMember] User User,
+ [property: DataMember] Dictionary Rates,
+ [property: DataMember] Dictionary RecipesList
+ );
+}
diff --git a/LocalServices/LocalEndpoint.cs b/LocalServices/LocalEndpoint.cs
new file mode 100644
index 0000000..c8f4533
--- /dev/null
+++ b/LocalServices/LocalEndpoint.cs
@@ -0,0 +1,65 @@
+using Endpoint;
+using LocalEndpoint.Data;
+using Models;
+using System.Collections.Immutable;
+
+namespace LocalEndpoint
+{
+
+ ///
+ /// The local endpoint is an implementation of the Endpoint API definition.
+ ///
+ ///
+ public class LocalEndpoint : IEndpoint
+ {
+ private readonly IAuthService authService;
+ private readonly IRecipesService recipesService;
+
+
+ public LocalEndpoint()
+ {
+ var db = new CatastrophicPerformancesDatabase(Environment.GetFolderPath(Environment.SpecialFolder.Personal));
+
+ if (db.IsEmpty())
+ PrepareDatabase(db);
+
+ recipesService = new RecipesService(db);
+ authService = new AuthService(db);
+ }
+
+ public IAuthService AuthService => authService;
+
+ public IRecipesService RecipesService => recipesService;
+
+ private static void PrepareDatabase(Database db)
+ {
+ 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", MakeGuid(1));
+ User USER2 = new User(Constants.DEFAULT_ACCOUNT_IMAGE, "Yanis", MakeGuid(2));
+ User USER3 = new User(Constants.DEFAULT_ACCOUNT_IMAGE, "Leo", MakeGuid(3));
+
+ db.InsertUser(USER1);
+ db.InsertUser(USER2);
+ db.InsertUser(USER3);
+
+ db.InsertAccount(new Account(USER1, "chief@cook.com"), "123456");
+ db.InsertAccount(new Account(USER2, "yanis@google.com"), "123456");
+ db.InsertAccount(new Account(USER3, "leo@google.com"), "123456");
+
+ db.InsertRecipe(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()), USER1, new List { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List { new PreparationStep("Step 1", "Bake the eggs") }.ToImmutableList()));
+ db.InsertRecipe(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()), USER2, new List { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List { new PreparationStep("Step 1", "Bake the eggs") }.ToImmutableList()));
+ db.InsertRecipe(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()), USER1, new List { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List { new PreparationStep("Step 1", "Bake the eggs") }.ToImmutableList()));
+ db.InsertRecipe(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()), USER3, new List { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List { new PreparationStep("Step 1", "Bake the eggs") }.ToImmutableList()));
+ db.InsertRecipe(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()), USER3, new List { new Ingredient("Ingredient 1", 6) }.ToImmutableList(), new List { new PreparationStep("Step 1", "Bake the eggs") }.ToImmutableList()));
+ db.InsertRecipe(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()), USER1, new List { new Ingredient("Chocolate", 4) }.ToImmutableList(), new List { new PreparationStep("Eat Chocolate", "Eat the chocolate") }.ToImmutableList()));
+ }
+
+ private static Guid MakeGuid(int seed)
+ {
+ var r = new Random(seed);
+ var guid = new byte[16];
+ r.NextBytes(guid);
+
+ return new Guid(guid);
+ }
+ }
+}
\ No newline at end of file
diff --git a/LocalEndpoint/LocalEndpoint.csproj b/LocalServices/LocalServices.csproj
similarity index 82%
rename from LocalEndpoint/LocalEndpoint.csproj
rename to LocalServices/LocalServices.csproj
index b84a3cb..62f7504 100644
--- a/LocalEndpoint/LocalEndpoint.csproj
+++ b/LocalServices/LocalServices.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/LocalEndpoint/RecipesService.cs b/LocalServices/RecipesService.cs
similarity index 54%
rename from LocalEndpoint/RecipesService.cs
rename to LocalServices/RecipesService.cs
index 9684e02..8969e9e 100644
--- a/LocalEndpoint/RecipesService.cs
+++ b/LocalServices/RecipesService.cs
@@ -1,4 +1,5 @@
using Endpoint;
+using LocalEndpoint.Data;
using Models;
using System.Collections.Immutable;
@@ -7,22 +8,22 @@ namespace LocalEndpoint
internal class RecipesService : IRecipesService
{
- private readonly RecipesDatabase db;
- private readonly Dictionary accountsData = new Dictionary();
+ private readonly Database db;
+ private readonly Dictionary accountsData = new Dictionary();
- public RecipesService(RecipesDatabase db)
+ public RecipesService(Database db)
{
this.db = db;
}
public ImmutableList PopularRecipes()
{
- return db.ListAll().Take(4).ToImmutableList().ConvertAll(v => v.Info);
+ return db.ListAllRecipes().Take(4).ToImmutableList().ConvertAll(v => v.Info);
}
public Recipe GetRecipe(RecipeInfo info)
{
- return db.Get(info.Id);
+ return db.GetRecipe(info.Id);
}
@@ -35,17 +36,16 @@ namespace LocalEndpoint
return GetOrInitData(account).Preferences;
}
- private AccountData GetOrInitData(Account account)
+ private AccountServices GetOrInitData(Account account)
{
- AccountData? data;
+ AccountServices? 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 { new Ingredient("Chocolate", 4) }.ToImmutableList(), new List { new PreparationStep("Eat Chocolate", "Eat the chocolate") }.ToImmutableList()));
AccountRecipesPreferences preferences = new AccountRecipesPreferences(account, db);
- data = new AccountData(recipes, preferences);
+ data = new AccountServices(recipes, preferences);
accountsData.Add(account, data);
}
return data;
diff --git a/Models/AccountRecipeRate.cs b/Models/AccountRecipeRate.cs
deleted file mode 100644
index caf7658..0000000
--- a/Models/AccountRecipeRate.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-
-namespace Models
-{
- public record AccountRecipeRate(bool IsFavorite = false, uint Rate = 0);
-}
diff --git a/Models/Ingredient.cs b/Models/Ingredient.cs
index 3a40a75..10e7655 100644
--- a/Models/Ingredient.cs
+++ b/Models/Ingredient.cs
@@ -1,4 +1,9 @@
-namespace Models
+using System.Runtime.Serialization;
+
+namespace Models
{
- public record Ingredient(string Name, float Amount);
+
+
+ [DataContract]
+ public record Ingredient([property: DataMember] string Name, [property: DataMember] float Amount);
}
diff --git a/Models/PreparationStep.cs b/Models/PreparationStep.cs
index 40899a7..35e4734 100644
--- a/Models/PreparationStep.cs
+++ b/Models/PreparationStep.cs
@@ -1,5 +1,7 @@
-namespace Models
-{
+using System.Runtime.Serialization;
- public record PreparationStep(string Name, string Description);
+namespace Models
+{
+ [DataContract]
+ public record PreparationStep([property: DataMember] string Name, [property: DataMember] string Description);
}
diff --git a/Models/Recipe.cs b/Models/Recipe.cs
index e077c22..021d99a 100644
--- a/Models/Recipe.cs
+++ b/Models/Recipe.cs
@@ -1,10 +1,14 @@
using System.Collections.Immutable;
+using System.Runtime.Serialization;
namespace Models
{
+ [DataContract]
public record Recipe(
- RecipeInfo Info,
- User Owner,
- ImmutableList Ingredients,
- ImmutableList Steps);
+ [property: DataMember] RecipeInfo Info,
+ [property: DataMember] User Owner,
+ [property: DataMember] ImmutableList Ingredients,
+ [property: DataMember] ImmutableList Steps
+ );
+
}
\ No newline at end of file
diff --git a/Models/RecipeInfo.cs b/Models/RecipeInfo.cs
index 1265c96..fec0ebb 100644
--- a/Models/RecipeInfo.cs
+++ b/Models/RecipeInfo.cs
@@ -1,10 +1,14 @@
-namespace Models
+using System.Runtime.Serialization;
+
+namespace Models
{
+ [DataContract]
public record RecipeInfo(
- string Name,
- uint CalPerPers,
- uint CookTimeMins,
- Uri? Image,
- float AverageNote,
- Guid Id);
+ [property: DataMember] string Name,
+ [property: DataMember] uint CalPerPers,
+ [property: DataMember] uint CookTimeMins,
+ [property: DataMember] Uri? Image,
+ [property: DataMember] float AverageNote,
+ [property: DataMember] Guid Id
+ );
}
diff --git a/Models/RecipeRate.cs b/Models/RecipeRate.cs
new file mode 100644
index 0000000..b4dc3a3
--- /dev/null
+++ b/Models/RecipeRate.cs
@@ -0,0 +1,11 @@
+
+using System.Runtime.Serialization;
+
+namespace Models
+{
+ [DataContract]
+ public record RecipeRate(
+ [property: DataMember] bool IsFavorite = false,
+ [property: DataMember] uint Rate = 0
+ );
+}
diff --git a/Models/User.cs b/Models/User.cs
index 981cef6..b70f7b1 100644
--- a/Models/User.cs
+++ b/Models/User.cs
@@ -1,4 +1,36 @@
-namespace Models
+using System.Runtime.Serialization;
+
+namespace Models
{
- public record User(Uri ProfilePicture, string Name);
+ [DataContract]
+ public class User
+ {
+ [DataMember]
+ public Uri ProfilePicture { get; init; }
+ [DataMember]
+ public string Name { get; init; }
+ [DataMember]
+ public Guid Id { get; init; }
+
+ public User(Uri profilePicture, string name, Guid id)
+ {
+ ProfilePicture = profilePicture;
+ Name = name;
+ Id = id;
+ }
+
+ public override bool Equals(object? other)
+ {
+ if (this == other)
+ return true;
+
+ User? otherUser = other as User;
+ return otherUser != null && Id.Equals(otherUser.Id);
+ }
+
+ override public int GetHashCode()
+ {
+ return Id.GetHashCode();
+ }
+ }
}
diff --git a/Platforms/Android/AndroidManifest.xml b/Platforms/Android/AndroidManifest.xml
index 60c24e0..7871058 100644
--- a/Platforms/Android/AndroidManifest.xml
+++ b/Platforms/Android/AndroidManifest.xml
@@ -3,4 +3,5 @@
+
\ No newline at end of file
diff --git a/Endpoint/IAccountOwnedRecipes.cs b/Services/IAccountOwnedRecipes.cs
similarity index 100%
rename from Endpoint/IAccountOwnedRecipes.cs
rename to Services/IAccountOwnedRecipes.cs
diff --git a/Endpoint/IAccountRecipesPreferences.cs b/Services/IAccountRecipesPreferences.cs
similarity index 91%
rename from Endpoint/IAccountRecipesPreferences.cs
rename to Services/IAccountRecipesPreferences.cs
index 1a40745..64d4f35 100644
--- a/Endpoint/IAccountRecipesPreferences.cs
+++ b/Services/IAccountRecipesPreferences.cs
@@ -13,7 +13,7 @@ namespace Endpoint
public void SetReviewScore(RecipeInfo info, uint score);
public bool AddToWeeklyList(RecipeInfo info, uint persAmount);
- public AccountRecipeRate GetRate(RecipeInfo info);
+ public RecipeRate GetRate(RecipeInfo info);
public ImmutableList GetFavorites();
diff --git a/Endpoint/IAccountManager.cs b/Services/IAuthService.cs
similarity index 84%
rename from Endpoint/IAccountManager.cs
rename to Services/IAuthService.cs
index c3b697b..0e450e2 100644
--- a/Endpoint/IAccountManager.cs
+++ b/Services/IAuthService.cs
@@ -1,7 +1,7 @@
using Models;
namespace Endpoint
{
- public interface IAccountManager
+ public interface IAuthService
{
public Account? Login(string email, string password);
diff --git a/Endpoint/IEndpoint.cs b/Services/IEndpoint.cs
similarity index 70%
rename from Endpoint/IEndpoint.cs
rename to Services/IEndpoint.cs
index 10906ee..b928557 100644
--- a/Endpoint/IEndpoint.cs
+++ b/Services/IEndpoint.cs
@@ -4,7 +4,7 @@ namespace Endpoint
{
public interface IEndpoint
{
- public IAccountManager AccountManager { get; }
+ public IAuthService AuthService { get; }
public IRecipesService RecipesService { get; }
diff --git a/Endpoint/IRecipesService.cs b/Services/IRecipesService.cs
similarity index 77%
rename from Endpoint/IRecipesService.cs
rename to Services/IRecipesService.cs
index f9159c2..88e8b65 100644
--- a/Endpoint/IRecipesService.cs
+++ b/Services/IRecipesService.cs
@@ -1,11 +1,6 @@
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
{
diff --git a/Endpoint/Endpoint.csproj b/Services/Services.csproj
similarity index 95%
rename from Endpoint/Endpoint.csproj
rename to Services/Services.csproj
index d24baee..e45d1d7 100644
--- a/Endpoint/Endpoint.csproj
+++ b/Services/Services.csproj
@@ -1,13 +1,13 @@
-
-
-
- net7.0
- enable
- enable
-
-
-
-
-
-
-
+
+
+
+ net7.0
+ enable
+ enable
+
+
+
+
+
+
+
diff --git a/ShoopNCook.csproj b/ShoopNCook.csproj
index 063bc5b..8a80861 100644
--- a/ShoopNCook.csproj
+++ b/ShoopNCook.csproj
@@ -48,28 +48,28 @@
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
@@ -118,8 +118,8 @@
-
-
+
+
diff --git a/ShoopNCook.sln b/ShoopNCook.sln
index 6ca3619..3b6c720 100644
--- a/ShoopNCook.sln
+++ b/ShoopNCook.sln
@@ -8,9 +8,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tests", "Tests\Tests.csproj
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Models", "Models\Models.csproj", "{A9D43E07-345D-4DD4-B4F9-CE69ED569B5F}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LocalEndpoint", "LocalEndpoint\LocalEndpoint.csproj", "{57732316-93B9-4DA0-A212-F8892D3D968B}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LocalServices", "LocalServices\LocalServices.csproj", "{57732316-93B9-4DA0-A212-F8892D3D968B}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Endpoint", "Endpoint\Endpoint.csproj", "{C976BDD8-710D-4162-8A42-973B634491F9}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services", "Services\Services.csproj", "{C976BDD8-710D-4162-8A42-973B634491F9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
diff --git a/Views/Components/RecipeView.xaml b/Views/Components/RecipeView.xaml
index 32922c3..624860f 100644
--- a/Views/Components/RecipeView.xaml
+++ b/Views/Components/RecipeView.xaml
@@ -17,6 +17,7 @@
MinimumWidthRequest="150"
MaximumWidthRequest="150"
RowDefinitions="*, Auto">
+
diff --git a/Views/FavoritesPage.xaml.cs b/Views/FavoritesPage.xaml.cs
index 8282f86..20b9ef2 100644
--- a/Views/FavoritesPage.xaml.cs
+++ b/Views/FavoritesPage.xaml.cs
@@ -3,10 +3,8 @@ using Models;
namespace ShoopNCook.Pages;
using Endpoint;
-using LocalEndpoint;
using Models;
using ShoopNCook.Views;
-using System.Security.Principal;
public partial class FavoritesPage : ContentPage
{
diff --git a/Views/RecipePage.xaml b/Views/RecipePage.xaml
index 63e2e0f..74fcf55 100644
--- a/Views/RecipePage.xaml
+++ b/Views/RecipePage.xaml
@@ -14,21 +14,20 @@
+ RowDefinitions="Auto, Auto, *, Auto">
+ AlignItems="Center"
+ HeightRequest="60">
-
@@ -111,7 +109,8 @@
+ Grid.Row="3"
+ MaximumHeightRequest="45">