Merge pull request #819 from eduardosmaniotto/bugfix/merchant-and-loading
bugfix: standardize loading states and add save spinner to Create pages (cherry picked from commit 7fb61714feea55d4c765b094c2e5f513ee5da3ba)
This commit is contained in:
@@ -11,18 +11,8 @@
|
||||
|
||||
@if (this._viewModel is null)
|
||||
{
|
||||
<span class="spinner-border" role="status" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">@Resources.Loading</span>
|
||||
return;
|
||||
}
|
||||
|
||||
@if (this._initState is { })
|
||||
{
|
||||
<span class="spinner-border" role="status" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">@this._initState</span>
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
<AutoForm Model="this._viewModel" OnValidSubmit="this.OnSaveButtonClickAsync"></AutoForm>
|
||||
<AutoForm Model="this._viewModel" OnValidSubmit="this.OnSaveButtonClickAsync" IsProcessing="this._isProcessing"></AutoForm>
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.Interfaces;
|
||||
using MUnique.OpenMU.Persistence;
|
||||
using MUnique.OpenMU.Web.AdminPanel.Properties;
|
||||
using MUnique.OpenMU.Web.Shared.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Razor page which shows objects of the specified type in a grid.
|
||||
@@ -22,7 +23,7 @@ public partial class CreateConnectServerConfig : ComponentBase, IAsyncDisposable
|
||||
private CancellationTokenSource? _disposeCts;
|
||||
|
||||
private ConnectServerViewModel? _viewModel;
|
||||
private string? _initState;
|
||||
private bool _isProcessing;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the context provider.
|
||||
@@ -54,6 +55,12 @@ public partial class CreateConnectServerConfig : ComponentBase, IAsyncDisposable
|
||||
[Inject]
|
||||
public NavigationManager NavigationManager { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the loading overlay service.
|
||||
/// </summary>
|
||||
[Inject]
|
||||
public LoadingOverlayService LoadingService { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
@@ -86,6 +93,7 @@ public partial class CreateConnectServerConfig : ComponentBase, IAsyncDisposable
|
||||
|
||||
private async Task LoadDataAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var loading = this.LoadingService.ShowLoadingIndicator();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var gameConfiguration = await this.DataSource.GetOwnerAsync(default, cancellationToken).ConfigureAwait(true);
|
||||
@@ -134,11 +142,11 @@ public partial class CreateConnectServerConfig : ComponentBase, IAsyncDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
var gameConfiguration = await this.DataSource.GetOwnerAsync().ConfigureAwait(false);
|
||||
|
||||
this._isProcessing = true;
|
||||
var gameConfiguration = await this.DataSource.GetOwnerAsync().ConfigureAwait(true);
|
||||
using var saveContext = this.ContextProvider.CreateNewTypedContext(typeof(DataModel.Configuration.ConnectServerDefinition), true, gameConfiguration);
|
||||
|
||||
var existingServerDefinitions = (await saveContext.GetAsync<ConnectServerDefinition>().ConfigureAwait(false)).ToList();
|
||||
var existingServerDefinitions = (await saveContext.GetAsync<ConnectServerDefinition>().ConfigureAwait(true)).ToList();
|
||||
if (existingServerDefinitions.Any(def => def.ServerId == this._viewModel?.ServerId))
|
||||
{
|
||||
this.ToastService.ShowError(string.Format(Resources.ServerWithIdAlreadyExists, this._viewModel?.ServerId));
|
||||
@@ -151,20 +159,13 @@ public partial class CreateConnectServerConfig : ComponentBase, IAsyncDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
this._initState = Resources.CreatingConfigurationInfo;
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
var connectServerDefinition = await this.CreateDefinitionByViewModelAsync(saveContext).ConfigureAwait(false);
|
||||
this._initState = Resources.SavingConfigurationInfo;
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
var connectServerDefinition = await this.CreateDefinitionByViewModelAsync(saveContext).ConfigureAwait(true);
|
||||
var success = await saveContext.SaveChangesAsync().ConfigureAwait(true);
|
||||
|
||||
// if success, init new game server instance
|
||||
if (success)
|
||||
{
|
||||
this.ToastService.ShowSuccess(Resources.ConnectionServerConfigurationSaved);
|
||||
this._initState = Resources.InitializingConnectServerInfo;
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
await this.ServerInstanceManager.InitializeConnectServerAsync(connectServerDefinition.ConfigurationId);
|
||||
await this.ServerInstanceManager.InitializeConnectServerAsync(connectServerDefinition.ConfigurationId).ConfigureAwait(true);
|
||||
this.NavigationManager.NavigateTo("servers");
|
||||
return;
|
||||
}
|
||||
@@ -175,8 +176,10 @@ public partial class CreateConnectServerConfig : ComponentBase, IAsyncDisposable
|
||||
{
|
||||
this.ToastService.ShowError(string.Format(Resources.UnexpectedErrorOccurred, ex.Message));
|
||||
}
|
||||
|
||||
this._initState = null;
|
||||
finally
|
||||
{
|
||||
this._isProcessing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -11,18 +11,8 @@
|
||||
|
||||
@if (this._viewModel is null)
|
||||
{
|
||||
<span class="spinner-border" role="status" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">@Resources.Loading</span>
|
||||
return;
|
||||
}
|
||||
|
||||
@if (this._initState is { })
|
||||
{
|
||||
<span class="spinner-border" role="status" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">@this._initState</span>
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
<AutoForm Model="this._viewModel" OnValidSubmit="this.OnSaveButtonClickAsync"></AutoForm>
|
||||
<AutoForm Model="this._viewModel" OnValidSubmit="this.OnSaveButtonClickAsync" IsProcessing="this._isProcessing"></AutoForm>
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ using MUnique.OpenMU.Interfaces;
|
||||
using MUnique.OpenMU.Persistence;
|
||||
using MUnique.OpenMU.Web.AdminPanel.Properties;
|
||||
using MUnique.OpenMU.Web.Shared.Components.Modal;
|
||||
using MUnique.OpenMU.Web.Shared.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Razor page that shows objects of the specified type in a grid.
|
||||
@@ -23,7 +24,7 @@ public partial class CreateGameServerConfig : ComponentBase, IAsyncDisposable
|
||||
private CancellationTokenSource? _disposeCts;
|
||||
|
||||
private GameServerViewModel? _viewModel;
|
||||
private string? _initState;
|
||||
private bool _isProcessing;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the context provider.
|
||||
@@ -61,6 +62,12 @@ public partial class CreateGameServerConfig : ComponentBase, IAsyncDisposable
|
||||
[Inject]
|
||||
public NavigationManager NavigationManager { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the loading overlay service.
|
||||
/// </summary>
|
||||
[Inject]
|
||||
public LoadingOverlayService LoadingService { get; set; } = null!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
@@ -93,6 +100,7 @@ public partial class CreateGameServerConfig : ComponentBase, IAsyncDisposable
|
||||
|
||||
private async Task LoadDataAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var loading = this.LoadingService.ShowLoadingIndicator();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
|
||||
var gameConfiguration = await this.DataSource.GetOwnerAsync(default, cancellationToken).ConfigureAwait(true);
|
||||
@@ -150,11 +158,11 @@ public partial class CreateGameServerConfig : ComponentBase, IAsyncDisposable
|
||||
{
|
||||
try
|
||||
{
|
||||
var gameConfiguration = await this.DataSource.GetOwnerAsync().ConfigureAwait(false);
|
||||
|
||||
this._isProcessing = true;
|
||||
var gameConfiguration = await this.DataSource.GetOwnerAsync().ConfigureAwait(true);
|
||||
using var saveContext = this.ContextProvider.CreateNewTypedContext(typeof(DataModel.Configuration.GameServerDefinition), true, gameConfiguration);
|
||||
|
||||
var existingServerDefinitions = (await saveContext.GetAsync<GameServerDefinition>().ConfigureAwait(false)).ToList();
|
||||
var existingServerDefinitions = (await saveContext.GetAsync<GameServerDefinition>().ConfigureAwait(true)).ToList();
|
||||
if (existingServerDefinitions.Any(def => def.ServerID == this._viewModel?.ServerId))
|
||||
{
|
||||
this.ToastService.ShowError(string.Format(Resources.ServerWithIdAlreadyExists, this._viewModel?.ServerId));
|
||||
@@ -167,20 +175,13 @@ public partial class CreateGameServerConfig : ComponentBase, IAsyncDisposable
|
||||
return;
|
||||
}
|
||||
|
||||
this._initState = Resources.CreatingConfigurationInfo;
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
var gameServerDefinition = await this.CreateDefinitionByViewModelAsync(saveContext).ConfigureAwait(false);
|
||||
this._initState = Resources.SavingConfigurationInfo;
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
var gameServerDefinition = await this.CreateDefinitionByViewModelAsync(saveContext).ConfigureAwait(true);
|
||||
var success = await saveContext.SaveChangesAsync().ConfigureAwait(true);
|
||||
|
||||
// if success, init new game server instance
|
||||
if (success)
|
||||
{
|
||||
this.ToastService.ShowSuccess(Resources.GameServerConfigurationSavedInfo);
|
||||
this._initState = Resources.InitializingGameServerInfo;
|
||||
await this.InvokeAsync(this.StateHasChanged);
|
||||
await this.ServerInstanceManager.InitializeGameServerAsync(gameServerDefinition.ServerID);
|
||||
await this.ServerInstanceManager.InitializeGameServerAsync(gameServerDefinition.ServerID).ConfigureAwait(true);
|
||||
this.NavigationManager.NavigateTo("servers");
|
||||
return;
|
||||
}
|
||||
@@ -191,8 +192,10 @@ public partial class CreateGameServerConfig : ComponentBase, IAsyncDisposable
|
||||
{
|
||||
this.ToastService.ShowError(string.Format(Resources.UnexpectedErrorOccurred, ex.Message));
|
||||
}
|
||||
|
||||
this._initState = null;
|
||||
finally
|
||||
{
|
||||
this._isProcessing = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -15,8 +15,6 @@
|
||||
|
||||
@if (this.ViewModels is null)
|
||||
{
|
||||
<span class="spinner-border" role="status" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">@Resources.Loading</span>
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -80,6 +80,12 @@ public partial class EditConfigGrid : ComponentBase, IAsyncDisposable
|
||||
[Inject]
|
||||
public ILogger<EditConfigGrid> Logger { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the loading overlay service.
|
||||
/// </summary>
|
||||
[Inject]
|
||||
public LoadingOverlayService LoadingService { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type.
|
||||
/// </summary>
|
||||
@@ -145,6 +151,7 @@ public partial class EditConfigGrid : ComponentBase, IAsyncDisposable
|
||||
|
||||
private async Task LoadDataAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var loading = this.LoadingService.ShowLoadingIndicator();
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
if (this.Type is null)
|
||||
{
|
||||
|
||||
@@ -13,8 +13,6 @@
|
||||
|
||||
@if (this.ViewModels is null)
|
||||
{
|
||||
<span class="spinner-border" role="status" aria-hidden="true"></span>
|
||||
<span class="visually-hidden">@Resources.Loading</span>
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ using MUnique.OpenMU.DataModel.Configuration;
|
||||
using MUnique.OpenMU.DataModel.Entities;
|
||||
using MUnique.OpenMU.Persistence;
|
||||
using MUnique.OpenMU.Web.AdminPanel.Properties;
|
||||
using MUnique.OpenMU.Web.Shared.Services;
|
||||
|
||||
/// <summary>
|
||||
/// Razor page which shows objects of the specified type in a grid.
|
||||
@@ -68,6 +69,12 @@ public partial class Merchants : ComponentBase, IAsyncDisposable
|
||||
[Inject]
|
||||
public ILogger<Merchants> Logger { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the loading overlay service.
|
||||
/// </summary>
|
||||
[Inject]
|
||||
public LoadingOverlayService LoadingService { get; set; } = null!;
|
||||
|
||||
private IQueryable<MerchantStorageViewModel>? ViewModels => this._viewModels?.AsQueryable();
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -134,6 +141,7 @@ public partial class Merchants : ComponentBase, IAsyncDisposable
|
||||
|
||||
private async Task LoadDataAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
using var loading = this.LoadingService.ShowLoadingIndicator();
|
||||
try
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
@@ -192,8 +200,13 @@ public partial class Merchants : ComponentBase, IAsyncDisposable
|
||||
{
|
||||
if (this._persistenceContext?.HasChanges is true)
|
||||
{
|
||||
var previousMerchantId = this._selectedMerchant?.Id;
|
||||
await this.DataSource.DiscardChangesAsync().ConfigureAwait(true);
|
||||
await this.LoadDataAsync(this._disposeCts?.Token ?? default).ConfigureAwait(true);
|
||||
if (previousMerchantId is { } id && this._viewModels is not null)
|
||||
{
|
||||
this._selectedMerchant = this._viewModels.FirstOrDefault(vm => vm.Id == id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,13 @@
|
||||
<AutoFields HideCollections="@this.HideCollections" SearchTerm="@this._currentSearchTerm" />
|
||||
<ValidationSummary />
|
||||
<div class="form-actions">
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<button type="submit" class="btn btn-primary" disabled="@this.IsProcessing">
|
||||
@if (this.IsProcessing)
|
||||
{
|
||||
<span class="spinner-border spinner-border-sm me-2" role="status" aria-hidden="true"></span>
|
||||
}
|
||||
@Resources.Save
|
||||
</button>
|
||||
@if (this.OnRefresh != null)
|
||||
{
|
||||
<button type="button" class="btn btn-secondary" @onclick="this.OnRefresh.Value" >@Resources.Refresh</button>
|
||||
|
||||
@@ -35,6 +35,14 @@ public partial class AutoForm<T>
|
||||
[Parameter]
|
||||
public EventCallback OnValidSubmit { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether the form is in a processing state,
|
||||
/// e.g. while saving data. When <see langword="true" />, the save button is
|
||||
/// disabled and shows a spinner.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool IsProcessing { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the callback invoked when the cancel button is clicked.
|
||||
/// When <see langword="null"/>, the cancel button is not rendered.
|
||||
|
||||
@@ -201,7 +201,7 @@
|
||||
<div class="form-actions">
|
||||
@if (this.OnValidSubmit.HasDelegate)
|
||||
{
|
||||
<button type="submit" class="btn btn-primary">Save</button>
|
||||
<button type="submit" class="btn btn-primary">@Resources.Save</button>
|
||||
}
|
||||
|
||||
@if (this.OnCancel.HasDelegate)
|
||||
|
||||
Reference in New Issue
Block a user