Merge pull request 'fix/app-ready' (#66) from fix/app-ready into dev
continuous-integration/drone/push Build is passing Details

Reviewed-on: #66
pull/67/head^2
Alexandre AGOSTINHO 2 years ago
commit c3fb6f1994

@ -12,5 +12,20 @@ namespace Model_UnitTests
Assert.NotNull(r.Title);
}
[Theory]
[InlineData("recipe", RecipeType.Dish, Priority.Light, "recipe", RecipeType.Dish, Priority.Light)]
[InlineData("No title.", RecipeType.Unspecified, Priority.Light, "", RecipeType.Unspecified, Priority.Light)]
[InlineData("re cipe", RecipeType.Unspecified, Priority.Light, "re cipe", RecipeType.Unspecified, Priority.Light)]
public void TestValuesConstructor(
string expectedTitle, RecipeType expectedType, Priority expectedPriority,
string title, RecipeType type, Priority priority)
{
Recipe rc = new Recipe(title, type, priority);
Assert.Equal(expectedTitle, rc.Title);
Assert.Equal(expectedType, rc.Type);
Assert.Equal(expectedPriority, rc.Priority);
}
}
}

@ -5,6 +5,7 @@ using Managers;
using System.Diagnostics;
using System.Runtime.Serialization;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.Serialization.Json;
namespace Views
{
@ -20,7 +21,7 @@ namespace Views
Debug.WriteLine("Hello, World!\n\n");
string path = FileSystem.Current.AppDataDirectory; // - path to the save file
string strategy = "xml"; // - strategy is 'xml' or 'json' (/!\ this is case sensitive)
string strategy = "json"; // - strategy is 'xml' or 'json' (/!\ this is case sensitive)
// Initialize the data serializer
IDataSerializer dataSerializer = (strategy == "xml") ?
@ -31,7 +32,7 @@ namespace Views
IDataManager dataManager;
if (!File.Exists(Path.Combine(path, $"data.{strategy}")))
{
var data = LoadXMLBundledFilesAsync("data.xml");
var data = LoadJSONBundledFilesAsync("data.json");
dataManager = new DataDefaultManager(dataSerializer, data);
}
else
@ -72,6 +73,7 @@ namespace Views
base.OnSleep();
}
/// <summary>
/// Load XML raw assets from data.
/// </summary>
@ -79,9 +81,8 @@ namespace Views
/// <returns>A dictionary containing the data loaded.</returns>
private static IDictionary<string, List<object>> LoadXMLBundledFilesAsync(string path)
{
//using Stream stream = await FileSystem.Current.OpenAppPackageFileAsync(path);
DataContractSerializerSettings _dataContractSerializerSettings
= new DataContractSerializerSettings()
DataContractSerializerSettings _dataContractSerializerSettings =
new DataContractSerializerSettings()
{
KnownTypes = new Type[]
{
@ -97,5 +98,29 @@ namespace Views
return data;
}
/// <summary>
/// Load JSON raw assets from data.
/// </summary>
/// <param name="path">The path in the raw assets directory.</param>
/// <returns>A dictionary containing the data loaded.</returns>
private static IDictionary<string, List<object>> LoadJSONBundledFilesAsync(string path)
{
DataContractJsonSerializerSettings _dataContractJsonSerializerSettings =
new DataContractJsonSerializerSettings()
{
KnownTypes = new Type[]
{
typeof(Recipe), typeof(RecipeType), typeof(Priority), typeof(Review), typeof(User), typeof(Ingredient), typeof(Quantity)
}
};
var jsonSerializer = new DataContractJsonSerializer(typeof(Dictionary<string, List<object>>), _dataContractJsonSerializerSettings);
IDictionary<string, List<Object>> data;
using Stream stream = FileSystem.Current.OpenAppPackageFileAsync(path).Result;
data = jsonSerializer.ReadObject(stream) as IDictionary<string, List<Object>>;
return data;
}
}
}

@ -13,6 +13,9 @@ namespace Views
private Ingredient ingredient;
private PreparationStep preparationStep;
private string titleRecipe;
public FileResult ImageSource { get; private set; } = null;
public string? ImageSourcePath { get; private set; } = null;
public MasterManager Master => (Application.Current as App).Master;
public User CurrentUser => Master.User.CurrentConnected;
public Recipe RecipeToAdd{ get=> recipeToAdd; set => recipeToAdd = value; }
@ -38,20 +41,30 @@ namespace Views
IngredientList = new List<Ingredient>();
PreparationStepList = new List<PreparationStep>();
}
private void PickPhoto(object sender, EventArgs e)
private async void PickPhoto(object sender, EventArgs e)
{
MediaPicker.PickPhotoAsync();
ImageSource = await MediaPicker.Default.PickPhotoAsync();
}
private void AddRecipeValidation(object sender, EventArgs e)
private async void AddRecipeValidation(object sender, EventArgs e)
{
if (string.IsNullOrWhiteSpace(TitleRecipe))
{
DisplayAlert("Erreur", "Entrez un nom de recette.", "Ok");
await DisplayAlert("Erreur", "Entrez un nom de recette.", "Ok");
return;
}
if (ImageSource != null)
{
// save the file into local storage
ImageSourcePath = Path.Combine(FileSystem.Current.AppDataDirectory, $"{TitleRecipe.Replace(" ", "")}.{ImageSource.FileName}");
using Stream sourceStream = await ImageSource.OpenReadAsync();
using FileStream localFileStream = File.OpenWrite(ImageSourcePath);
await sourceStream.CopyToAsync(localFileStream);
}
RecipeType newRecipeType = GetSelectedRecipeType();
Priority selectedPriority = GetSelectedPriority();
string authorMail = CurrentUser.Mail;
@ -62,26 +75,35 @@ namespace Views
newRecipeType,
selectedPriority,
null,
authorMail
authorMail,
ImageSourcePath
);
newRecipe.PreparationSteps.AddRange(PreparationStepList);
newRecipe.Ingredients.AddRange(IngredientList);
bool isRecipeSave = Master.Recipe.AddRecipeToData(newRecipe);
// Save data.
Debug.Write($"[ {DateTime.Now:H:mm:ss} ] Saving...\t");
Master.Data.SaveData();
Debug.WriteLine("Done.");
Debug.WriteLine(FileSystem.Current.AppDataDirectory);
if (isRecipeSave)
{
DisplayAlert("Succès", "La recette a été ajoutée avec succès", "OK");
await DisplayAlert("Succès", "La recette a été ajoutée avec succès", "OK");
}
else
{
DisplayAlert("Echec", "La recette n'a pas été ajoutée", "OK");
await DisplayAlert("Echec", "La recette n'a pas été ajoutée", "OK");
}
newRecipe = new Recipe("Nouvelle Recette");
PreparationStepList.Clear();
IngredientList.Clear();
Navigation.PopAsync();
await Navigation.PopModalAsync();
}
private void AddStepRecipe(object sender, EventArgs e)
@ -115,6 +137,12 @@ namespace Views
private void AddIngredient(object sender, EventArgs e)
{
if (nameIngredient.Text is null || quantityNumber.Text is null)
{
DisplayAlert("Warning", "some values are null, please provide correct values.", "Ok");
return;
}
string ingredientName = nameIngredient.Text;
int numberQuantity = Convert.ToInt32(quantityNumber.Text);
Unit unitQuantity = (Unit)UnitPicker.SelectedItem;

@ -1,5 +1,6 @@
using AppException;
using Model;
using System.Diagnostics;
namespace Views;
@ -24,6 +25,13 @@ public partial class Login : ContentPage
try
{
usermgr.AddUserToData(usermgr.CreateUser(mail, password));
// Save data.
Debug.Write($"[ {DateTime.Now:H:mm:ss} ] Saving...\t");
Master.Data.SaveData();
Debug.WriteLine("Done.");
Debug.WriteLine(FileSystem.Current.AppDataDirectory);
}
catch (BadMailFormatException)
{

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:model="clr-namespace:Model;assembly=Model"
xmlns:local="clr-namespace:Views"
x:Class="Views.MyPosts"
Title="MyPosts">
@ -8,45 +9,59 @@
<local:ContainerBase
NeedReturn="True">
<!-- Flyout -->
<local:ContainerBase.MyFlyoutContent>
<Grid RowDefinitions="250, *, *" VerticalOptions="Fill">
<Grid RowDefinitions="Auto, *, *" VerticalOptions="Center">
<VerticalStackLayout Grid.Row="1">
<Button Text="Mes informations" ImageSource="person_default.png" Style="{StaticResource button1}" Grid.Row="1"/>
<Button Text="Modifier" ImageSource="settings_icon.png" Style="{StaticResource button1}" Grid.Row="2"/>
<Button Text="Mes informations"
ImageSource="person_default.png"
Style="{StaticResource button1}"
Grid.Row="1"
Clicked="MyInformations_Clicked"/>
<Button Text="Ajouter une recette"
ImageSource="add_icon.png"
Style="{StaticResource button1}"
Grid.Row="2"
Clicked="AddRecipe_Clicked"/>
</VerticalStackLayout>
</Grid>
</local:ContainerBase.MyFlyoutContent>
<!-- Master -->
<local:ContainerBase.MyContent>
<ScrollView>
<StackLayout>
<Label Text="Mon profil" TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontAttributes="Bold"
FontSize="24" Padding="15, 15, 20, 5"/>
<Label Text="Mes publications" TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontSize="20" Padding="15"/>
<StackLayout MinimumWidthRequest="400">
<Label
Text="{Binding RecipesDisplayed.Description}"
TextColor="{AppThemeBinding Light={StaticResource Black}, Dark={StaticResource Gray100}}"
FontSize="24"
Padding="15"/>
<FlexLayout
Margin="0, 15"
Wrap="Wrap"
JustifyContent="Start"
AlignItems="Center"
AlignContent="SpaceEvenly"
HorizontalOptions="Center">
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
<local:RecipeCase CaseImageSource="room_service_icon.png"/>
Margin="0, 15"
Wrap="Wrap"
JustifyContent="Start"
AlignItems="Center"
AlignContent="SpaceEvenly"
HorizontalOptions="Center"
BindableLayout.ItemsSource="{Binding RecipesDisplayed}">
<BindableLayout.ItemTemplate>
<DataTemplate x:DataType="model:Recipe">
<local:RecipeCase
CaseImageSource="{Binding Image}"
RecipeTitle="{Binding Title}"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
</StackLayout>
</ScrollView>
</local:ContainerBase.MyContent>
</local:ContainerBase>

@ -1,9 +1,34 @@
using Model;
namespace Views;
public partial class MyPosts : ContentPage
{
public MyPosts()
public MasterManager Master => (Application.Current as App).Master;
private readonly RecipeCollection _recipesDisplayed;
public ReadOnlyObservableRecipeCollection RecipesDisplayed { get; private set; }
public Recipe Recipe => Master.Recipe.CurrentSelected;
public MyPosts()
{
InitializeComponent();
}
_recipesDisplayed = Master.Recipe.GetRecipeByAuthor(Master.User.CurrentConnected.Mail);
RecipesDisplayed = new ReadOnlyObservableRecipeCollection(_recipesDisplayed);
InitializeComponent();
BindingContext = this;
}
private void MyInformations_Clicked(object sender, EventArgs e)
{
Navigation.PopModalAsync();
}
private void AddRecipe_Clicked(object sender, EventArgs e)
{
Navigation.PushModalAsync(new AddRecipe());
}
}

@ -9,17 +9,17 @@
<local:EnumToValuesConverter x:Key="musicTypeToValueConverter"
x:TypeArguments="model:Priority"/>
</ContentPage.Resources>
<local:ContainerBase>
<local:ContainerBase.MyFlyoutContent NeedReturn="True">
<Grid RowDefinitions="250, *, *" VerticalOptions="Fill">
<local:ContainerBase NeedReturn="True">
<local:ContainerBase.MyFlyoutContent>
<Grid RowDefinitions="Auto, *, *" VerticalOptions="Center">
<VerticalStackLayout Grid.Row="1">
<Button Text="Mes Recettes"
ImageSource="person_default.png"
Style="{StaticResource button1}"
Grid.Row="1"
Clicked="OnMyRecipeClicked"/>
<Button Text="Ajouter Recette"
ImageSource="settings_icon.png"
<Button Text="Ajouter une recette"
ImageSource="add_icon.png"
Style="{StaticResource button1}"
Grid.Row="2"
Clicked="OnAddRecipeClicked"/>

@ -12,7 +12,7 @@
<Grid RowDefinitions="Auto, *">
<!-- Return -->
<local:ReturnButton NeedReturn="{Binding NeedReturn}" Grid.Row="0"
<local:ReturnButton NeedReturn="{Binding NeedReturn, Source={x:Reference fl}}" Grid.Row="0"
HorizontalOptions="Start" Padding="10, 10, 0, 0"/>
<!-- Header -->
<ImageButton Source="person_default.png" HorizontalOptions="Center"

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="M450-200v-250H200v-60h250v-250h60v250h250v60H510v250h-60Z"/></svg>

After

Width:  |  Height:  |  Size: 163 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

@ -0,0 +1,376 @@
[
{
"Key": "Recipe",
"Value": [
{
"__type": "recipe:#Model",
"authorMail": "admin@mctg.fr",
"id": 9806,
"image": "cookies1.jpg",
"ingredient": [
{
"id": "Patates",
"quantity": {
"digit": 23,
"unit": 0
}
},
{
"id": "Farine",
"quantity": {
"digit": 23,
"unit": 3
}
}
],
"preparation-steps": [
{
"description": "Faire cuire.",
"order": 1
},
{
"description": "Manger.",
"order": 2
}
],
"priority": 2,
"reviews": [
{
"authorMail": "admin@mctg.fr",
"content": "Bonne recette, je recommande !",
"id": 30967,
"stars": 4
},
{
"authorMail": "admin@mctg.fr",
"content": "Bof bof, mais mangeable...",
"id": 27370,
"stars": 3
}
],
"title": "Cookies classiques",
"type": 3
},
{
"__type": "recipe:#Model",
"authorMail": "admin@mctg.fr",
"id": 4678,
"image": "cookies2.jpg",
"ingredient": [
{
"id": "Farine",
"quantity": {
"digit": 200,
"unit": 3
}
}
],
"preparation-steps": [
{
"description": "Moulinez la pâte.",
"order": 1
},
{
"description": "Faire cuire pendant une bonne heure.",
"order": 2
},
{
"description": "Sortir du four et mettre dans un plat.",
"order": 3
}
],
"priority": 1,
"reviews": [],
"title": "Cookies au chocolat",
"type": 3
},
{
"__type": "recipe:#Model",
"authorMail": "admin@mctg.fr",
"id": 28213,
"image": "room_service_icon.png",
"ingredient": [
{
"id": "Farine",
"quantity": {
"digit": 200,
"unit": 3
}
},
{
"id": "Lait",
"quantity": {
"digit": 2,
"unit": 4
}
}
],
"preparation-steps": [
{
"description": "Achetez les ingrédients.",
"order": 1
},
{
"description": "Préparez le matériel. Ustensiles et tout.",
"order": 2
},
{
"description": "Pleurez.",
"order": 3
}
],
"priority": 4,
"reviews": [
{
"authorMail": "pedrosamigos@hotmail.com",
"content": "C'était vraiment IN-CROY-ABLE !!!",
"id": 5127,
"stars": 5
}
],
"title": "Gateau nature",
"type": 3
},
{
"__type": "recipe:#Model",
"authorMail": "admin@mctg.fr",
"id": 27448,
"image": "room_service_icon.png",
"ingredient": [ ],
"preparation-steps": [
{
"description": "Achetez les légumes.",
"order": 1
},
{
"description": "Préparez le plat. Ustensiles et préchauffez le four.",
"order": 2
},
{
"description": "Coupez les pommes en morceaux et disposez-les sur le plat.",
"order": 3
},
{
"description": "Mettez enfin le plat au four, puis une fois cuit, dégustez !",
"order": 4
}
],
"priority": 3,
"reviews": [ ],
"title": "Gateau au pommes",
"type": 3
},
{
"__type": "recipe:#Model",
"authorMail": "pedrosamigos@hotmail.com",
"id": 14217,
"image": "room_service_icon.png",
"ingredient": [
{
"id": "Mais",
"quantity": {
"digit": 2,
"unit": 1
}
},
{
"id": "Sachet pépites de chocolat",
"quantity": {
"digit": 1,
"unit": 0
}
},
{
"id": "Dinde",
"quantity": {
"digit": 2,
"unit": 3
}
}
],
"preparation-steps": [
{
"description": "Ajouter les oeufs.",
"order": 1
},
{
"description": "Ajouter la farine.",
"order": 2
},
{
"description": "Ajouter 100g de chocolat fondu.",
"order": 3
},
{
"description": "Mélanger le tout.",
"order": 4
},
{
"description": "Faire cuire 45h au four traditionnel.",
"order": 5
}
],
"priority": 0,
"reviews": [ ],
"title": "Gateau au chocolat",
"type": 3
},
{
"__type": "recipe:#Model",
"authorMail": "pedrosamigos@hotmail.com",
"id": 3856,
"image": "room_service_icon.png",
"ingredient": [
{
"id": "Morceaux de bois",
"quantity": {
"digit": 2,
"unit": 0
}
},
{
"id": "Sachet gélatine",
"quantity": {
"digit": 1,
"unit": 0
}
},
{
"id": "Jambon",
"quantity": {
"digit": 2,
"unit": 1
}
}
],
"preparation-steps": [
{
"description": "Faire une cuisson bien sec de la dinde à la poêle",
"order": 1
},
{
"description": "Mettre la dinde au frigo.",
"order": 2
},
{
"description": "Mettre le jambon dans le micro-onde.",
"order": 3
},
{
"description": "Faire chauffer 3min.",
"order": 4
},
{
"description": "Présentez sur un plat la dinde et le jambon : Miam !",
"order": 5
}
],
"priority": 2,
"reviews": [ ],
"title": "Dinde au jambon",
"type": 2
},
{
"__type": "recipe:#Model",
"authorMail": "pedrosamigos@hotmail.com",
"id": 29272,
"image": "room_service_icon.png",
"ingredient": [
{
"id": "Pissenlis",
"quantity": {
"digit": 200,
"unit": 0
}
},
{
"id": "Boule de pétanque",
"quantity": {
"digit": 10,
"unit": 0
}
},
{
"id": "Poivre",
"quantity": {
"digit": 4,
"unit": 2
}
}
],
"preparation-steps": [
{
"description": "Trouvez des épices de curry.",
"order": 1
},
{
"description": "Trouvez maintenant du poulet.",
"order": 2
},
{
"description": "Coupez la tête du poulet et posez-la dans un plat.",
"order": 3
},
{
"description": "Parsemez d'épices curry la tête de la poule.",
"order": 4
},
{
"description": "Mettre le tout au four traditionnel 30min.",
"order": 5
},
{
"description": "Dégustez en famille !",
"order": 6
}
],
"priority": 4,
"reviews": [
{
"authorMail": "admin@mctg.fr",
"content": "Meilleure recette que j'ai avalé de tout les temps !!!!!!!",
"id": 7846,
"stars": 5
}
],
"title": "Poulet au curry",
"type": 2
}
]
},
{
"Key": "User",
"Value": [
{
"__type": "user:#Model",
"hashedpass": "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918",
"mail": "admin@mctg.fr",
"name": "Admin",
"priorities": [
4,
0,
1,
3,
2
],
"profilepic": "default_picture.png",
"surname": "Admin"
},
{
"__type": "user:#Model",
"hashedpass": "df7415f099b2e105822cb6052a0de0a4eb6a4c4060b5ea191bff1271e1c377fa",
"mail": "pedrosamigos@hotmail.com",
"name": "Pedros",
"priorities": [
4,
0,
1,
3,
2
],
"profilepic": "default_picture.png",
"surname": "Amigos"
}
]
}
]
Loading…
Cancel
Save