From e7f564659f0ced9d160c6172735b160000a08f79 Mon Sep 17 00:00:00 2001
From: NeoCodeMatrix86 <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Tue, 15 Sep 2026 16:41:36 +0200
Subject: [PATCH 1/8] Update GenerateCuesheetDialog.razor
---
.../Dialogs/GenerateCuesheetDialog.razor | 79 ++++++++-----------
1 file changed, 35 insertions(+), 44 deletions(-)
diff --git a/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor b/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor
index 374ef511..504cffdf 100644
--- a/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor
+++ b/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor
@@ -24,49 +24,35 @@ along with Foobar. If not, see
- @if (String.IsNullOrEmpty(GetGenerationValidationMessages()) == false)
+ @if (String.IsNullOrEmpty(_validationErrorMessage) == false)
{
@_localizer["Export is currently not possible:"]
- @((MarkupString)GetGenerationValidationMessages()!)
+ @((MarkupString)_validationErrorMessage)
}
-
-
-
- @_localizer["Name"]
- @_localizer["Content"]
-
-
- @context.Name
- @{
- var ariaLabel = $"Download-{context.Name}";
-
- }
-
-
-
+ Error="String.IsNullOrEmpty(_fileNameValidationErrorMessage) == false" ErrorText="@_fileNameValidationErrorMessage" />
+
+ @_localizer["Download"]
+
@code {
- //TODO: Get validation messages only once during rendering and use the reference for better performance
- //TODO: Will be a single file when #548 gets done
- IEnumerable _exportfiles = [];
DownloadOptions? _options;
+ String? _fileNameValidationErrorMessage = null;
+ String? _validationErrorMessage = null;
+
+ [CascadingParameter]
+ private IMudDialogInstance? MudDialog { get; set; }
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
_options = await base.LocalStorageOptionsProvider.GetOptionsAsync();
base.LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved;
- var result = _cuesheetExportService.GenerateExportfile(_options?.CuesheetFilename);
- if (result.IsSuccess)
- {
- _exportfiles = [result.Value!];
- }
+ SetValidationErrors();
}
protected override void Dispose(bool disposing)
@@ -75,46 +61,51 @@ along with Foobar. If not, see
base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved;
}
- String? GetValidationErrorMessage()
+ async Task OnDownloadClick()
{
- String? validationErrorMessage = null;
- if (_options != null)
+ MudDialog?.Close();
+ var generateExportResult = _cuesheetExportService.GenerateExportfile(_options!.CuesheetFilename);
+ if (generateExportResult.IsSuccess == false)
{
- var validationMessages = _validationService.Validate(_options, nameof(DownloadOptions.CuesheetFilename));
- if (validationMessages.Count() > 0)
- {
- validationErrorMessage = String.Join(Environment.NewLine, validationMessages);
- }
+ throw new InvalidOperationException($"Could not generate export file: {generateExportResult.Error?.Message}");
}
- return validationErrorMessage;
+ var exportfile = generateExportResult.Value!;
+ await _blazorDownloadFileService.DownloadFile(exportfile.Name, exportfile.Content, "text/plain");
}
- String? GetGenerationValidationMessages()
+ void SetValidationErrors()
{
- String? validationErrorMessage = null;
+ _validationErrorMessage = null;
var result = _cuesheetExportService.CanGenerateExportfile(_options?.CuesheetFilename);
if (result.IsSuccess == false)
{
- validationErrorMessage = result.Error?.Message.Replace(Environment.NewLine, "
");
+ _validationErrorMessage = result.Error?.Message.Replace(Environment.NewLine, "
");
}
- return validationErrorMessage;
+ SetFileNameValidationError();
}
- async Task CuesheetFilenameChanged(string newFilename)
+ void SetFileNameValidationError()
{
- await LocalStorageOptionsProvider.SaveOptionsValueAsync(x => x.CuesheetFilename, newFilename);
- var result = _cuesheetExportService.GenerateExportfile(_options?.CuesheetFilename);
- if (result.IsSuccess)
+ _fileNameValidationErrorMessage = null;
+ var validationMessages = _validationService.Validate(_options, nameof(DownloadOptions.CuesheetFilename));
+ if (validationMessages.Count() > 0)
{
- _exportfiles = [result.Value!];
+ _fileNameValidationErrorMessage = String.Join(Environment.NewLine, validationMessages);
}
}
+ async Task CuesheetFilenameChanged(string? newFilename)
+ {
+ //TODO: an empty filename is not valid and results in options not beeing stored
+ await LocalStorageOptionsProvider.SaveOptionsValueAsync(x => x.CuesheetFilename, newFilename);
+ }
+
void LocalStorageOptionsProvider_OptionSaved(object? sender, IOptions option)
{
if (option is DownloadOptions downloadOptions)
{
_options = downloadOptions;
+ SetValidationErrors();
StateHasChanged();
}
}
From e260c411d2bbd700e570ef3396243c531c0e75a8 Mon Sep 17 00:00:00 2001
From: NeoCodeMatrix86 <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Tue, 15 Sep 2026 16:55:43 +0200
Subject: [PATCH 2/8] set validation error during download
---
.../Options/ILocalStorageOptionsProvider.cs | 5 ++-
.../Options/LocalStorageOptionsProvider.cs | 37 +++++++++++++------
.../Dialogs/GenerateCuesheetDialog.razor | 11 ++++--
3 files changed, 35 insertions(+), 18 deletions(-)
diff --git a/AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs b/AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs
index e96fee9d..b57f91b7 100644
--- a/AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs
+++ b/AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs
@@ -14,6 +14,7 @@
//along with Foobar. If not, see
//.
using AudioCuesheetEditor.Model.Options;
+using AudioCuesheetEditor.Services;
using System.Linq.Expressions;
namespace AudioCuesheetEditor.Data.Options
@@ -22,8 +23,8 @@ public interface ILocalStorageOptionsProvider
{
event EventHandler? OptionSaved;
Task GetOptionsAsync() where T : IOptions;
- Task SaveOptionsAsync(IOptions options);
- Task SaveOptionsValueAsync(Expression> propertyExpression, object? value) where T : class, IOptions, new();
+ Task SaveOptionsAsync(IOptions options);
+ Task SaveOptionsValueAsync(Expression> propertyExpression, object? value) where T : class, IOptions, new();
Task SaveNestedOptionValueAsync(Expression> nestedPropertyExpression, Expression> valuePropertyExpression, TValue value) where T : class, IOptions, new();
}
}
diff --git a/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs b/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs
index 10a70da7..f987b81b 100644
--- a/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs
+++ b/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs
@@ -15,6 +15,8 @@
//.
using AudioCuesheetEditor.Model.Entity;
using AudioCuesheetEditor.Model.Options;
+using AudioCuesheetEditor.Services;
+using Microsoft.Extensions.Localization;
using Microsoft.JSInterop;
using System.Linq.Expressions;
using System.Reflection;
@@ -22,13 +24,13 @@
namespace AudioCuesheetEditor.Data.Options
{
- public class LocalStorageOptionsProvider(IJSRuntime jsRuntime): ILocalStorageOptionsProvider
+ public class LocalStorageOptionsProvider(IJSRuntime jsRuntime, IStringLocalizer localizer) : ILocalStorageOptionsProvider
{
public event EventHandler? OptionSaved;
private readonly IJSRuntime _jsRuntime = jsRuntime;
-
- private readonly JsonSerializerOptions SerializerOptions = new()
+ private readonly IStringLocalizer _localizer = localizer;
+ private readonly JsonSerializerOptions _serializerOptions = new()
{
DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
};
@@ -61,22 +63,26 @@ public async Task GetOptionsAsync() where T : IOptions
}
}
- public async Task SaveOptionsAsync(IOptions options)
+ public async Task SaveOptionsAsync(IOptions options)
{
- bool saveOptions = true;
if (options is IValidateable validateable)
{
- saveOptions = validateable.Validate().Status != ValidationStatus.Error;
+ var validationResult = validateable.Validate();
+ if (validationResult.Status == ValidationStatus.Error)
+ {
+ return Result.Failure(new Error(ErrorType.ValidationFailed, string.Join(Environment.NewLine, validationResult.ValidationMessages.Select(x => x.GetMessageLocalized(_localizer)))));
+ }
+ await WriteOptionsAsync(options);
+ return Result.Success();
}
- if (saveOptions)
+ else
{
- var optionsJson = JsonSerializer.Serialize