Merge pull request 'Add toasts to notify user' (#55) from user-notifier into master
continuous-integration/drone/push Build is passing Details

Reviewed-on: ShopNCook/ShopNCook#55
master
Maxime BATISTA 2 years ago
commit e6ccd28f20

@ -1,4 +1,7 @@
[*.cs] [*.cs]
# CS0618: Le type ou le membre est obsolète
dotnet_diagnostic.CS0618.severity = silent
# CS1998: Async method lacks 'await' operators and will run synchronously # CS1998: Async method lacks 'await' operators and will run synchronously
dotnet_diagnostic.CS1998.severity = none dotnet_diagnostic.CS1998.severity = none

@ -8,8 +8,6 @@ public partial class App : Application, ConnectionObserver, IApp
private IEndpoint Endpoint = new LocalEndpoint(); private IEndpoint Endpoint = new LocalEndpoint();
public IUserNotifier Notifier => new ConsoleUserNotifier();
public App() public App()
{ {
InitializeComponent(); InitializeComponent();
@ -25,7 +23,7 @@ public partial class App : Application, ConnectionObserver, IApp
public void ForceLogin() public void ForceLogin()
{ {
Shell shell = new ConnectAppShell(this, Endpoint.AuthService, Notifier); Shell shell = new ConnectAppShell(this, Endpoint.AuthService);
shell.GoToAsync("//Splash"); shell.GoToAsync("//Splash");
MainPage = shell; MainPage = shell;
} }

@ -7,9 +7,9 @@ using ShoopNCook.Pages;
public partial class ConnectAppShell : Shell public partial class ConnectAppShell : Shell
{ {
public ConnectAppShell(ConnectionObserver observer, IAuthService accounts, IUserNotifier notifier) public ConnectAppShell(ConnectionObserver observer, IAuthService accounts)
{ {
ConnectionController controller = new ConnectionController(observer, accounts, notifier); ConnectionController controller = new ConnectionController(observer, accounts);
InitializeComponent(); InitializeComponent();
LoginPage.ContentTemplate = new DataTemplate(() => new LoginPage(controller)); LoginPage.ContentTemplate = new DataTemplate(() => new LoginPage(controller));
RegisterPage.ContentTemplate = new DataTemplate(() => new RegisterPage(controller)); RegisterPage.ContentTemplate = new DataTemplate(() => new RegisterPage(controller));

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

@ -7,19 +7,27 @@ namespace ShoopNCook.Controllers
{ {
private readonly ConnectionObserver observer; private readonly ConnectionObserver observer;
private readonly IAuthService accounts; private readonly IAuthService accounts;
private readonly IUserNotifier notifier; public ConnectionController(ConnectionObserver observer, IAuthService accounts) {
public ConnectionController(ConnectionObserver observer, IAuthService accounts, IUserNotifier notifier) {
this.observer = observer; this.observer = observer;
this.accounts = accounts; this.accounts = accounts;
this.notifier = notifier;
} }
public void Login(string email, string password) public void Login(string email, string password)
{ {
if (email == null)
{
UserNotifier.Notice("Please provide an email address");
return;
}
if (password == null)
{
UserNotifier.Notice("Please provide your password");
return;
}
Account? acc = accounts.Login(email, password); Account? acc = accounts.Login(email, password);
if (acc == null) if (acc == null)
{ {
notifier.Error("Email or password invalid."); UserNotifier.Error("Email or password invalid.");
return; return;
} }
observer.OnAccountConnected(acc); observer.OnAccountConnected(acc);
@ -27,10 +35,25 @@ namespace ShoopNCook.Controllers
public void Register(string username, string email, string password) public void Register(string username, string email, string password)
{ {
if (email == null)
{
UserNotifier.Notice("Please provide an email address");
return;
}
if (password == null)
{
UserNotifier.Notice("Please provide your password");
return;
}
if (username == null)
{
UserNotifier.Notice("Please provide an username");
return;
}
Account? acc = accounts.Register(username, email, password); Account? acc = accounts.Register(username, email, password);
if (acc == null) if (acc == null)
{ {
notifier.Error("Invalid credentials."); UserNotifier.Error("Invalid credentials.");
return; return;
} }
observer.OnAccountConnected(acc); observer.OnAccountConnected(acc);

@ -20,13 +20,13 @@ namespace ShoopNCook.Controllers
public void Logout() public void Logout()
{ {
app.Notifier.Notice("You have been loged out."); UserNotifier.Notice("You have been loged out.");
app.ForceLogin(); app.ForceLogin();
} }
public async void GoToMyRecipesPage() public async void GoToMyRecipesPage()
{ {
await Shell.Current.Navigation.PushAsync(new MyRecipesPage(account, endpoint.RecipesService, app.Notifier)); await Shell.Current.Navigation.PushAsync(new MyRecipesPage(account, endpoint.RecipesService));
} }
public async void GoToProfilePage() public async void GoToProfilePage()

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

@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShoopNCook
{
public interface IUserNotifier
{
public void Success(string message);
public void Notice(string message);
public void Error(string message);
public void Warn(string message);
}
}

@ -9,9 +9,9 @@ public partial class MainAppShell : Shell
public MainAppShell(Account account, IEndpoint endpoint, IApp app) public MainAppShell(Account account, IEndpoint endpoint, IApp app)
{ {
InitializeComponent(); InitializeComponent();
HomeTab.ContentTemplate = new DataTemplate(() => new HomePage(account, app.Notifier, endpoint)); HomeTab.ContentTemplate = new DataTemplate(() => new HomePage(account, endpoint));
FavoritesTab.ContentTemplate = new DataTemplate(() => new FavoritesPage(account, app.Notifier, endpoint.RecipesService)); FavoritesTab.ContentTemplate = new DataTemplate(() => new FavoritesPage(account, endpoint.RecipesService));
MyListTab.ContentTemplate = new DataTemplate(() => new MyListPage(account, app.Notifier, endpoint.RecipesService)); MyListTab.ContentTemplate = new DataTemplate(() => new MyListPage(account, endpoint.RecipesService));
MoreTab.ContentTemplate = new DataTemplate(() => new MorePage(account, new MorePageController(account, endpoint, app))); MoreTab.ContentTemplate = new DataTemplate(() => new MorePage(account, new MorePageController(account, endpoint, app)));
} }
} }

@ -1,4 +1,5 @@
using Microsoft.Extensions.Logging; using CommunityToolkit.Maui;
using Microsoft.Extensions.Logging;
namespace ShoopNCook; namespace ShoopNCook;
@ -9,6 +10,7 @@ public static class MauiProgram
var builder = MauiApp.CreateBuilder(); var builder = MauiApp.CreateBuilder();
builder builder
.UseMauiApp<App>() .UseMauiApp<App>()
.UseMauiCommunityToolkit()
.ConfigureFonts(fonts => .ConfigureFonts(fonts =>
{ {
fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular"); fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 499 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

@ -113,6 +113,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CommunityToolkit.Maui" Version="5.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" /> <PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="7.0.0" />
<PackageReference Include="xunit" Version="2.4.2" /> <PackageReference Include="xunit" Version="2.4.2" />
</ItemGroup> </ItemGroup>
@ -142,6 +143,9 @@
<MauiXaml Update="ConnectAppShell.xaml"> <MauiXaml Update="ConnectAppShell.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</MauiXaml> </MauiXaml>
<MauiXaml Update="Views\Components\NoticePopup.xaml">
<Generator>MSBuild:Compile</Generator>
</MauiXaml>
<MauiXaml Update="Views\CreateRecipePage.xaml"> <MauiXaml Update="Views\CreateRecipePage.xaml">
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>
</MauiXaml> </MauiXaml>

@ -10,7 +10,12 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Models", "Models\Models.csp
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LocalServices", "LocalServices\LocalServices.csproj", "{57732316-93B9-4DA0-A212-F8892D3D968B}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LocalServices", "LocalServices\LocalServices.csproj", "{57732316-93B9-4DA0-A212-F8892D3D968B}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services", "Services\Services.csproj", "{C976BDD8-710D-4162-8A42-973B634491F9}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Services", "Services\Services.csproj", "{C976BDD8-710D-4162-8A42-973B634491F9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6DEA92EF-71CD-4A21-9CC0-67F228E1155D}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
EndProjectSection
EndProject EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution

@ -0,0 +1,48 @@
using CommunityToolkit.Maui.Alerts;
using CommunityToolkit.Maui.Core;
using CommunityToolkit.Maui.Views;
using ShoopNCook.Views.Components;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ShoopNCook
{
internal class UserNotifier
{
private static async Task Show(string message, string messageType)
{
CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
// Vous pouvez configurer la durée et la taille de police ici.
ToastDuration duration = ToastDuration.Short;
double fontSize = 14;
var toast = Toast.Make(message, duration, fontSize);
await toast.Show(cancellationTokenSource.Token);
}
public static void Error(string message)
{
Show(message, "Error");
}
public static void Warn(string message)
{
Show(message, "Warning");
}
public static void Notice(string message)
{
Show(message, "Notice");
}
public static void Success(string message)
{
Show(message, "Success");
}
}
}

@ -0,0 +1,10 @@
<ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ShoopNCook.Views.Components.NoticePopup">
<VerticalStackLayout>
<Label
x:Name="MessageLabel"
VerticalOptions="Center"
HorizontalOptions="Center" />
</VerticalStackLayout>
</ContentView>

@ -0,0 +1,39 @@
namespace ShoopNCook.Views.Components;
using Microsoft.Maui.Graphics;
public partial class NoticePopup : ContentView
{
public NoticePopup(string message, string messageType)
{
InitializeComponent();
MessageLabel.Text = message;
switch (messageType)
{
case "Error":
this.BackgroundColor = Microsoft.Maui.Graphics.Colors.Red;
break;
case "Warning":
this.BackgroundColor = Microsoft.Maui.Graphics.Colors.Yellow;
break;
case "Notice":
this.BackgroundColor = Microsoft.Maui.Graphics.Colors.Blue;
break;
case "Success":
this.BackgroundColor = Microsoft.Maui.Graphics.Colors.Green;
break;
}
// Display the toast for 3 seconds
Device.StartTimer(TimeSpan.FromSeconds(3), () =>
{
// Close the toast
// You need to replace this with your actual code to close the toast
this.IsVisible = false;
return false;
});
}
}

@ -8,31 +8,29 @@ public partial class CreateRecipePage : ContentPage
private User owner; private User owner;
private Action<Recipe> onRecipeCreated; private Action<Recipe> onRecipeCreated;
private IUserNotifier notifier;
public CreateRecipePage(User owner, IUserNotifier notifier, Action<Recipe> onRecipeCreated) public CreateRecipePage(User owner, Action<Recipe> onRecipeCreated)
{ {
InitializeComponent(); InitializeComponent();
this.owner = owner; this.owner = owner;
this.onRecipeCreated = onRecipeCreated; this.onRecipeCreated = onRecipeCreated;
this.notifier = notifier;
} }
private async void OnAddIngredientTapped(object sender, TappedEventArgs e) private void OnAddIngredientTapped(object sender, TappedEventArgs e)
{ {
IngredientList.Children.Add(new IngredientEntry()); IngredientList.Children.Add(new IngredientEntry());
} }
private async void OnAddStepTapped(object sender, TappedEventArgs e) private void OnAddStepTapped(object sender, TappedEventArgs e)
{ {
StepList.Children.Add(new StepEntry((uint) StepList.Children.Count() + 1)); StepList.Children.Add(new StepEntry((uint) StepList.Children.Count() + 1));
} }
private async void OnBackButtonClicked(object sender, EventArgs e) private async void OnBackButtonClicked(object sender, EventArgs e)
{ {
Navigation.PopAsync(); await Navigation.PopAsync();
} }
private async void OnUploadRecipeClicked(object sender, EventArgs e) private void OnUploadRecipeClicked(object sender, EventArgs e)
{ {
uint callPerPers; uint callPerPers;
@ -53,7 +51,7 @@ public partial class CreateRecipePage : ContentPage
if (hadErrors) if (hadErrors)
{ {
notifier.Error("You need to fix input errors before upload."); UserNotifier.Error("You need to fix input errors before upload.");
return; return;
} }

@ -10,14 +10,12 @@ public partial class FavoritesPage : ContentPage
{ {
private readonly Account account; private readonly Account account;
private readonly IUserNotifier notifier;
private IRecipesService service; private IRecipesService service;
public FavoritesPage(Account account, IUserNotifier notifier, IRecipesService service) public FavoritesPage(Account account, IRecipesService service)
{ {
InitializeComponent(); InitializeComponent();
this.account = account; this.account = account;
this.notifier = notifier;
this.service = service; this.service = service;
UpdateFavorites(); UpdateFavorites();
@ -31,13 +29,13 @@ public partial class FavoritesPage : ContentPage
{ {
RecipeViewLayout.Children.Add(new RecipeView(info, async () => RecipeViewLayout.Children.Add(new RecipeView(info, async () =>
{ {
Recipe? recipe = service.GetRecipe(info); Recipe recipe = service.GetRecipe(info);
await Shell.Current.Navigation.PushAsync(new RecipePage(recipe, notifier, preferences, 1)); await Shell.Current.Navigation.PushAsync(new RecipePage(recipe, preferences, 1));
})); }));
}); });
} }
private async void ContentPage_NavigatedTo(object sender, NavigatedToEventArgs e) private void ContentPage_NavigatedTo(object sender, NavigatedToEventArgs e)
{ {
UpdateFavorites(); UpdateFavorites();
} }

@ -1,4 +1,3 @@
namespace ShoopNCook.Pages; namespace ShoopNCook.Pages;
using Models; using Models;
using ShoopNCook.Views; using ShoopNCook.Views;
@ -7,7 +6,7 @@ using LocalEndpoint;
public partial class HomePage : ContentPage public partial class HomePage : ContentPage
{ {
public HomePage(Account account, IUserNotifier notifier, IEndpoint endpoint) public HomePage(Account account, IEndpoint endpoint)
{ {
InitializeComponent(); InitializeComponent();
@ -20,12 +19,12 @@ public partial class HomePage : ContentPage
{ {
layout.Children.Add(new RecipeView(info, () => layout.Children.Add(new RecipeView(info, () =>
{ {
Recipe? recipe = service.GetRecipe(info); Recipe recipe = service.GetRecipe(info);
if (recipe != null) if (recipe != null)
Shell.Current.Navigation.PushAsync(new RecipePage(recipe, notifier, preferences, 1)); Shell.Current.Navigation.PushAsync(new RecipePage(recipe, preferences, 1));
else else
{ {
notifier.Error("Could not find recipe"); UserNotifier.Error("Could not find recipe");
} }
})); }));
} }
@ -37,9 +36,6 @@ public partial class HomePage : ContentPage
ProfilePictureName.Text = account.User.Name; ProfilePictureName.Text = account.User.Name;
} }
private async void OnSyncButtonClicked(object sender, EventArgs e) private async void OnSyncButtonClicked(object sender, EventArgs e)
{ {
await Shell.Current.Navigation.PushAsync(new SearchPage()); await Shell.Current.Navigation.PushAsync(new SearchPage());

@ -11,7 +11,7 @@ public partial class LoginPage : ContentPage
InitializeComponent(); InitializeComponent();
this.controller = controller; this.controller = controller;
} }
private async void OnLoginButtonClicked(object sender, EventArgs e) private void OnLoginButtonClicked(object sender, EventArgs e)
{ {
string email = EmailEntry.Text; string email = EmailEntry.Text;
string password = PasswordEntry.Text; string password = PasswordEntry.Text;

@ -9,15 +9,13 @@ public partial class MyListPage : ContentPage
{ {
private readonly IAccountRecipesPreferences preferences; private readonly IAccountRecipesPreferences preferences;
private readonly IUserNotifier notifier;
private readonly IRecipesService service; private readonly IRecipesService service;
public MyListPage(Account account, IUserNotifier notifier, IRecipesService service) public MyListPage(Account account, IRecipesService service)
{ {
InitializeComponent(); InitializeComponent();
this.preferences = service.GetPreferencesOf(account); this.preferences = service.GetPreferencesOf(account);
this.notifier = notifier;
this.service = service; this.service = service;
UpdateMyList(); UpdateMyList();
@ -32,7 +30,7 @@ public partial class MyListPage : ContentPage
RecipesLayout.Children.Add(new StoredRecipeView(info, tuple.Item2, amount => RecipesLayout.Children.Add(new StoredRecipeView(info, tuple.Item2, amount =>
{ {
Recipe recipe = service.GetRecipe(info); Recipe recipe = service.GetRecipe(info);
Shell.Current.Navigation.PushAsync(new RecipePage(recipe, notifier, preferences, amount)); Shell.Current.Navigation.PushAsync(new RecipePage(recipe, preferences, amount));
})); }));
}); });
} }

@ -8,18 +8,15 @@ namespace ShoopNCook.Pages;
public partial class MyRecipesPage : ContentPage public partial class MyRecipesPage : ContentPage
{ {
private IUserNotifier notifier;
private IRecipesService service; private IRecipesService service;
private Account account; private Account account;
public MyRecipesPage( public MyRecipesPage(
Account account, Account account,
IRecipesService service, IRecipesService service)
IUserNotifier notifier)
{ {
InitializeComponent(); InitializeComponent();
this.notifier = notifier;
this.service = service; this.service = service;
this.account = account; this.account = account;
@ -35,7 +32,7 @@ public partial class MyRecipesPage : ContentPage
{ {
Recipe recipe = service.GetRecipe(info); Recipe recipe = service.GetRecipe(info);
IAccountRecipesPreferences preferences = service.GetPreferencesOf(account); IAccountRecipesPreferences preferences = service.GetPreferencesOf(account);
Shell.Current.Navigation.PushAsync(new RecipePage(recipe, notifier, preferences, 1)); Shell.Current.Navigation.PushAsync(new RecipePage(recipe, preferences, 1));
}, },
() => RemoveRecipe(info) () => RemoveRecipe(info)
)); ));
@ -47,7 +44,7 @@ public partial class MyRecipesPage : ContentPage
if (!recipes.RemoveRecipe(info)) if (!recipes.RemoveRecipe(info))
{ {
notifier.Error("Could not remove recipe"); UserNotifier.Error("Could not remove recipe");
return; return;
} }
foreach (OwnedRecipeView view in RecipesLayout.Children) foreach (OwnedRecipeView view in RecipesLayout.Children)
@ -58,7 +55,7 @@ public partial class MyRecipesPage : ContentPage
break; break;
} }
} }
notifier.Success("Recipe successfully removed"); UserNotifier.Success("Recipe successfully removed");
} }
private async void OnBackButtonClicked(object sender, EventArgs e) private async void OnBackButtonClicked(object sender, EventArgs e)
@ -69,14 +66,14 @@ public partial class MyRecipesPage : ContentPage
{ {
IAccountOwnedRecipes recipes = service.GetRecipesOf(account); IAccountOwnedRecipes recipes = service.GetRecipesOf(account);
var page = new CreateRecipePage(account.User, notifier, recipe => var page = new CreateRecipePage(account.User, recipe =>
{ {
if (!recipes.UploadRecipe(recipe)) if (!recipes.UploadRecipe(recipe))
{ {
notifier.Error("Could not upload recipe."); UserNotifier.Error("Could not upload recipe.");
return; return;
} }
notifier.Success("Recipe Successfuly uploaded !"); UserNotifier.Success("Recipe Successfuly uploaded !");
AddRecipeView(recipe.Info); AddRecipeView(recipe.Info);
Shell.Current.Navigation.PopAsync(); //go back to current recipe page. Shell.Current.Navigation.PopAsync(); //go back to current recipe page.
}); });

@ -14,17 +14,15 @@ public partial class RecipePage : ContentPage
private IAccountRecipesPreferences preferences; private IAccountRecipesPreferences preferences;
private IUserNotifier notifier;
private RecipeInfo info; private RecipeInfo info;
public ICommand StarCommand => new Command<string>(count => SetNote(uint.Parse(count))); public ICommand StarCommand => new Command<string>(count => SetNote(uint.Parse(count)));
public RecipePage(Recipe recipe, IUserNotifier notifier, IAccountRecipesPreferences preferences, uint amount) public RecipePage(Recipe recipe, IAccountRecipesPreferences preferences, uint amount)
{ {
InitializeComponent(); InitializeComponent();
this.preferences = preferences; this.preferences = preferences;
this.notifier = notifier;
this.info = recipe.Info; this.info = recipe.Info;
RecipeRate rate = preferences.GetRate(recipe.Info); RecipeRate rate = preferences.GetRate(recipe.Info);
@ -89,15 +87,15 @@ public partial class RecipePage : ContentPage
private async void OnSubmitReviewClicked(object o, EventArgs e) private async void OnSubmitReviewClicked(object o, EventArgs e)
{ {
preferences.SetReviewScore(info, note); preferences.SetReviewScore(info, note);
notifier.Success("Your review has been successfuly submited"); UserNotifier.Success("Your review has been successfuly submited");
} }
private async void OnAddToMyListClicked(object o, EventArgs e) private async void OnAddToMyListClicked(object o, EventArgs e)
{ {
if (!preferences.AddToWeeklyList(info, Counter.Count)) if (!preferences.AddToWeeklyList(info, Counter.Count))
notifier.Notice("You already added this recipe to you weekly list!"); UserNotifier.Notice("You already added this recipe to you weekly list!");
else else
notifier.Success("Recipe added to your weekly list."); UserNotifier.Success("Recipe added to your weekly list.");
} }
private void SetFavorite(bool isFavorite) private void SetFavorite(bool isFavorite)

Loading…
Cancel
Save