manage conflict (generic way) use toast to show error (#588)

* manage conflict (generic way) use toast to show error

* fix httpservice after merge conflict, adapt to use new repository
This commit is contained in:
Cédric Michel
2021-10-25 23:32:07 +02:00
committed by GitHub
parent 935167daaf
commit 8f55b1b56a
17 changed files with 337 additions and 29 deletions

View File

@@ -0,0 +1,88 @@
using BlazorAdmin.Services;
using Microsoft.AspNetCore.Components;
using System;
namespace BlazorAdmin.Helpers
{
public class ToastComponent : ComponentBase, IDisposable
{
[Inject]
ToastService ToastService
{
get;
set;
}
protected string Heading
{
get;
set;
}
protected string Message
{
get;
set;
}
protected bool IsVisible
{
get;
set;
}
protected string BackgroundCssClass
{
get;
set;
}
protected string IconCssClass
{
get;
set;
}
protected override void OnInitialized()
{
ToastService.OnShow += ShowToast;
ToastService.OnHide += HideToast;
}
private void ShowToast(string message, ToastLevel level)
{
BuildToastSettings(level, message);
IsVisible = true;
StateHasChanged();
}
private void HideToast()
{
IsVisible = false;
StateHasChanged();
}
private void BuildToastSettings(ToastLevel level, string message)
{
switch (level)
{
case ToastLevel.Info:
BackgroundCssClass = "bg-info";
IconCssClass = "info";
Heading = "Info";
break;
case ToastLevel.Success:
BackgroundCssClass = "bg-success";
IconCssClass = "check";
Heading = "Success";
break;
case ToastLevel.Warning:
BackgroundCssClass = "bg-warning";
IconCssClass = "exclamation";
Heading = "Warning";
break;
case ToastLevel.Error:
BackgroundCssClass = "bg-danger";
IconCssClass = "times";
Heading = "Error";
break;
}
Message = message;
}
public void Dispose()
{
ToastService.OnShow -= ShowToast;
}
}
}

View File

@@ -106,13 +106,13 @@
@code {
[Parameter]
public IEnumerable<CatalogBrand> Brands { get; set; }
[Parameter]
public IEnumerable<CatalogType> Types { get; set; }
[Parameter]
public IEnumerable<CatalogBrand> Brands { get; set; }
[Parameter]
public IEnumerable<CatalogType> Types { get; set; }
[Parameter]
public EventCallback<string> OnSaveClick { get; set; }
[Parameter]
public EventCallback<string> OnSaveClick { get; set; }
private string LoadPicture => string.IsNullOrEmpty(_item.PictureBase64) ? string.Empty : $"data:image/png;base64, {_item.PictureBase64}";
private bool HasPicture => !string.IsNullOrEmpty(_item.PictureBase64);
@@ -124,13 +124,17 @@
private async Task CreateClick()
{
await CatalogItemService.Create(_item);
await OnSaveClick.InvokeAsync(null);
await Close();
var result = await CatalogItemService.Create(_item);
if (result != null)
{
await OnSaveClick.InvokeAsync(null);
await Close();
}
}
public async Task Open()
{
Logger.LogInformation("Now loading... /Catalog/Create");
await new Css(JSRuntime).HideBodyOverflow();

View File

@@ -25,14 +25,16 @@ namespace BlazorAdmin
builder.Services.AddScoped(sp => new HttpClient() { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
builder.Services.AddScoped<ToastService>();
builder.Services.AddScoped<HttpService>();
builder.Services.AddScoped<ILocalStorageService, LocalStorageService>();
builder.Services.AddAuthorizationCore();
builder.Services.AddScoped<AuthenticationStateProvider, CustomAuthStateProvider>();
builder.Services.AddScoped(sp => (CustomAuthStateProvider)sp.GetRequiredService<AuthenticationStateProvider>());
builder.Services.AddBlazorServices();
builder.Logging.AddConfiguration(builder.Configuration.GetSection("Logging"));

View File

@@ -33,7 +33,8 @@ namespace BlazorAdmin.Services
public async Task<CatalogItem> Create(CreateCatalogItemRequest catalogItem)
{
return (await _httpService.HttpPost<CreateCatalogItemResponse>("catalog-items", catalogItem)).CatalogItem;
var response = await _httpService.HttpPost<CreateCatalogItemResponse>("catalog-items", catalogItem);
return response?.CatalogItem;
}
public async Task<CatalogItem> Edit(CatalogItem catalogItem)

View File

@@ -1,4 +1,5 @@
using BlazorShared;
using BlazorShared.Models;
using System.Net.Http;
using System.Text;
using System.Text.Json;
@@ -9,11 +10,14 @@ namespace BlazorAdmin.Services
public class HttpService
{
private readonly HttpClient _httpClient;
private readonly ToastService _toastService;
private readonly string _apiUrl;
public HttpService(HttpClient httpClient, BaseUrlConfiguration baseUrlConfiguration)
public HttpService(HttpClient httpClient, BaseUrlConfiguration baseUrlConfiguration, ToastService toastService)
{
_httpClient = httpClient;
_toastService = toastService;
_apiUrl = baseUrlConfiguration.ApiBase;
}
@@ -49,6 +53,12 @@ namespace BlazorAdmin.Services
var result = await _httpClient.PostAsync($"{_apiUrl}{uri}", content);
if (!result.IsSuccessStatusCode)
{
var exception = JsonSerializer.Deserialize<ErrorDetails>(await result.Content.ReadAsStringAsync(), new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
_toastService.ShowToast($"Error : {exception.Message}", ToastLevel.Error);
return null;
}
@@ -63,13 +73,13 @@ namespace BlazorAdmin.Services
var result = await _httpClient.PutAsync($"{_apiUrl}{uri}", content);
if (!result.IsSuccessStatusCode)
{
_toastService.ShowToast("Error", ToastLevel.Error);
return null;
}
return await FromHttpResponseMessage<T>(result);
}
private StringContent ToJson(object obj)
{
return new StringContent(JsonSerializer.Serialize(obj), Encoding.UTF8, "application/json");

View File

@@ -0,0 +1,55 @@
using System;
using System.Timers;
namespace BlazorAdmin.Services
{
public enum ToastLevel
{
Info,
Success,
Warning,
Error
}
public class ToastService : IDisposable
{
public event Action<string, ToastLevel> OnShow;
public event Action OnHide;
private Timer Countdown;
public void ShowToast(string message, ToastLevel level)
{
OnShow?.Invoke(message, level);
StartCountdown();
}
private void StartCountdown()
{
SetCountdown();
if (Countdown.Enabled)
{
Countdown.Stop();
Countdown.Start();
}
else
{
Countdown.Start();
}
}
private void SetCountdown()
{
if (Countdown == null)
{
Countdown = new Timer(3000);
Countdown.Elapsed += HideToast;
Countdown.AutoReset = false;
}
}
private void HideToast(object source, ElapsedEventArgs args)
{
OnHide?.Invoke();
}
public void Dispose()
{
Countdown?.Dispose();
}
}
}

View File

@@ -3,6 +3,7 @@
@inherits BlazorAdmin.Helpers.BlazorLayoutComponent
<AuthorizeView Roles=@BlazorShared.Authorization.Constants.Roles.ADMINISTRATORS>
<div class="sidebar">
<NavMenu />
@@ -10,11 +11,13 @@
</AuthorizeView>
<div class="main">
<div class="top-row px-4">
<a href="https://github.com/dotnet-architecture/eShopOnWeb" target="_blank" class="ml-md-auto">About eShopOnWeb</a>
</div>
<div class="content px-4">
<Toast></Toast>
@Body
</div>
</div>

View File

@@ -0,0 +1,13 @@
@inherits BlazorAdmin.Helpers.ToastComponent
@namespace BlazorAdmin.Shared
<div class="toast @(IsVisible ? "toast-visible" : null) @BackgroundCssClass">
<div class="toast-icon">
<i class="fa fa-@IconCssClass" aria-hidden="true"></i>
</div>
<div class="toast-body">
<h5>@Heading</h5>
<p>@Message</p>
</div>
</div>

View File

@@ -153,6 +153,53 @@ a, .btn-link {
overflow: hidden !important;
}
.toast {
display: none;
padding: 1.5rem;
color: #fff;
z-index: 99999;
position: absolute;
width: 25rem;
top: 2rem;
border-radius: 1rem;
left: 50%;
}
.toast-icon {
display: flex;
flex-direction: column;
justify-content: center;
padding: 01rem;
font-size: 2.5rem;
}
.toast-body {
display: flex;
flex-direction: column;
flex: 1;
padding-left: 1rem;
}
.toast-body p {
margin-bottom: 0;
}
.toast-visible {
display: flex;
flex-direction: row;
animation: fadein 1.5s;
}
@keyframes fadein {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@media (max-width: 767.98px) {
.main .top-row:not(.auth) {
display: none;