-

+
Loading application, please stand by ...
@@ -55,7 +54,7 @@
🗙
-
+
From 749553b5c502877b14c43fe70a2c612ae18738ba Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Fri, 20 Feb 2026 15:18:36 +0100
Subject: [PATCH 16/48] use culture for switching culture
---
.../Services/UI/LocalizationService.cs | 16 ++++++++--------
AudioCuesheetEditor/Shared/AppBar.razor | 6 +++---
2 files changed, 11 insertions(+), 11 deletions(-)
diff --git a/AudioCuesheetEditor/Services/UI/LocalizationService.cs b/AudioCuesheetEditor/Services/UI/LocalizationService.cs
index c48c8588..96934225 100644
--- a/AudioCuesheetEditor/Services/UI/LocalizationService.cs
+++ b/AudioCuesheetEditor/Services/UI/LocalizationService.cs
@@ -46,27 +46,27 @@ public async Task SetCultureFromConfigurationAsync()
{
var options = await _localStorageOptionsProvider.GetOptionsAsync
();
- ChangeLanguage(options.Culture.Name);
+ ChangeLanguage(options.Culture);
}
- public async Task ChangeLanguageAsync(string name)
+ public async Task ChangeLanguageAsync(CultureInfo culture)
{
- if (ChangeLanguage(name))
+ if (ChangeLanguage(culture))
{
- await _localStorageOptionsProvider.SaveOptionsValueAsync(x => x.CultureName!, name);
+ await _localStorageOptionsProvider.SaveOptionsValueAsync(x => x.CultureName!, culture.Name);
LocalizationChanged?.Invoke(this, new EventArgs());
}
}
- private static Boolean ChangeLanguage(string name)
+ private static Boolean ChangeLanguage(CultureInfo newCulture)
{
- var newCulture = AvailableCultures.SingleOrDefault(c => c.Name == name);
- if (newCulture != null)
+ var contains = AvailableCultures.Contains(newCulture);
+ if (contains)
{
CultureInfo.DefaultThreadCurrentUICulture = newCulture;
CultureInfo.CurrentUICulture = newCulture;
}
- return newCulture != null;
+ return contains;
}
}
}
diff --git a/AudioCuesheetEditor/Shared/AppBar.razor b/AudioCuesheetEditor/Shared/AppBar.razor
index 9631153d..4fbda6c8 100644
--- a/AudioCuesheetEditor/Shared/AppBar.razor
+++ b/AudioCuesheetEditor/Shared/AppBar.razor
@@ -59,7 +59,7 @@ along with Foobar. If not, see
@foreach (var culture in LocalizationService.AvailableCultures)
{
- @IsoCountryCodeToFlagEmoji(culture) @culture.DisplayName
+ @IsoCountryCodeToFlagEmoji(culture) @culture.DisplayName
}
@@ -115,9 +115,9 @@ along with Foobar. If not, see
[Parameter]
public Boolean DisplayReset { get; set; }
- async Task SelectedCultureChanged(string name)
+ async Task SelectedCultureChanged(CultureInfo culture)
{
- await base.LocalizationService.ChangeLanguageAsync(name);
+ await base.LocalizationService.ChangeLanguageAsync(culture);
}
String GetStyle(CultureInfo cultureInfo)
From a45051ba1fde408a7468fd4096eebd01c7849956 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Fri, 27 Feb 2026 11:00:21 +0100
Subject: [PATCH 17/48] reload after switching language
---
.../Services/UI/LocalizationService.cs | 13 ++--
AudioCuesheetEditor/Shared/AppBar.razor | 10 +++-
.../Shared/BaseLocalizedComponent.cs | 7 ---
.../BaseLocalizedLayoutComponentBase.cs | 59 -------------------
.../Shared/Layouts/MainLayout.razor | 5 +-
.../Layouts/MainLayoutWithoutMenu.razor | 3 +-
6 files changed, 19 insertions(+), 78 deletions(-)
delete mode 100644 AudioCuesheetEditor/Shared/Layouts/BaseLocalizedLayoutComponentBase.cs
diff --git a/AudioCuesheetEditor/Services/UI/LocalizationService.cs b/AudioCuesheetEditor/Services/UI/LocalizationService.cs
index 96934225..f158f979 100644
--- a/AudioCuesheetEditor/Services/UI/LocalizationService.cs
+++ b/AudioCuesheetEditor/Services/UI/LocalizationService.cs
@@ -15,13 +15,17 @@
//.
using AudioCuesheetEditor.Data.Options;
using AudioCuesheetEditor.Model.Options;
+using Microsoft.AspNetCore.Components;
+using Microsoft.JSInterop;
using System.Globalization;
namespace AudioCuesheetEditor.Services.UI
{
- public class LocalizationService(ILocalStorageOptionsProvider localStorageOptionsProvider)
+ public class LocalizationService(ILocalStorageOptionsProvider localStorageOptionsProvider, NavigationManager navigationManager, IJSRuntime jsRuntime)
{
private readonly ILocalStorageOptionsProvider _localStorageOptionsProvider = localStorageOptionsProvider;
+ private readonly NavigationManager _navigationManager = navigationManager;
+ private readonly IJSRuntime _jsRuntime = jsRuntime;
public const String DefaultCulture = "en-US";
@@ -38,8 +42,6 @@ public static IReadOnlyCollection AvailableCultures
}
}
- public event EventHandler? LocalizationChanged;
-
public static CultureInfo SelectedCulture => CultureInfo.DefaultThreadCurrentUICulture ?? CultureInfo.CurrentUICulture;
public async Task SetCultureFromConfigurationAsync()
@@ -54,7 +56,8 @@ public async Task ChangeLanguageAsync(CultureInfo culture)
if (ChangeLanguage(culture))
{
await _localStorageOptionsProvider.SaveOptionsValueAsync(x => x.CultureName!, culture.Name);
- LocalizationChanged?.Invoke(this, new EventArgs());
+ await _jsRuntime.InvokeVoidAsync("removeBeforeunload");
+ _navigationManager.NavigateTo(_navigationManager.Uri, true);
}
}
@@ -64,7 +67,7 @@ private static Boolean ChangeLanguage(CultureInfo newCulture)
if (contains)
{
CultureInfo.DefaultThreadCurrentUICulture = newCulture;
- CultureInfo.CurrentUICulture = newCulture;
+ CultureInfo.DefaultThreadCurrentCulture = newCulture;
}
return contains;
}
diff --git a/AudioCuesheetEditor/Shared/AppBar.razor b/AudioCuesheetEditor/Shared/AppBar.razor
index 4fbda6c8..771d40f1 100644
--- a/AudioCuesheetEditor/Shared/AppBar.razor
+++ b/AudioCuesheetEditor/Shared/AppBar.razor
@@ -69,7 +69,10 @@ along with Foobar. If not, see
}
@_localizer["Help"]
@_localizer["About"]
- @_localizer["Hotkeys"]
+ @if (DisplayHotkeys)
+ {
+ @_localizer["Hotkeys"]
+ }
@_localizer["Preview environment"]
@if (DisplayReset)
{
@@ -115,6 +118,9 @@ along with Foobar. If not, see
[Parameter]
public Boolean DisplayReset { get; set; }
+ [Parameter]
+ public Boolean DisplayHotkeys { get; set; }
+
async Task SelectedCultureChanged(CultureInfo culture)
{
await base.LocalizationService.ChangeLanguageAsync(culture);
@@ -223,7 +229,7 @@ along with Foobar. If not, see
await _importManager.UploadFilesAsync([file]);
}
- async Task DisplayHotkeys()
+ async Task ShowHotkeysDialog()
{
var options = new DialogOptions() { BackdropClick = false, CloseOnEscapeKey = true, CloseButton = true, FullWidth = true, MaxWidth = MaxWidth.Large };
await _dialogService.ShowAsync(_localizer["Hotkeys"], options);
diff --git a/AudioCuesheetEditor/Shared/BaseLocalizedComponent.cs b/AudioCuesheetEditor/Shared/BaseLocalizedComponent.cs
index 5a31acad..ddb56da7 100644
--- a/AudioCuesheetEditor/Shared/BaseLocalizedComponent.cs
+++ b/AudioCuesheetEditor/Shared/BaseLocalizedComponent.cs
@@ -33,7 +33,6 @@ public abstract class BaseLocalizedComponent : ComponentBase, IDisposable
protected override void OnInitialized()
{
base.OnInitialized();
- LocalizationService.LocalizationChanged += LocalizationService_LocalizationChanged;
TraceChangeManager.TracedObjectHistoryChanged += TraceChangeManager_TracedObjectHistoryChanged;
TraceChangeManager.UndoDone += TraceChangeManager_UndoDone;
TraceChangeManager.RedoDone += TraceChangeManager_RedoDone;
@@ -54,18 +53,12 @@ void TraceChangeManager_TracedObjectHistoryChanged(object? sender, EventArgs e)
StateHasChanged();
}
- void LocalizationService_LocalizationChanged(object? sender, EventArgs args)
- {
- StateHasChanged();
- }
-
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
- LocalizationService.LocalizationChanged -= LocalizationService_LocalizationChanged;
TraceChangeManager.TracedObjectHistoryChanged -= TraceChangeManager_TracedObjectHistoryChanged;
TraceChangeManager.UndoDone -= TraceChangeManager_UndoDone;
TraceChangeManager.RedoDone -= TraceChangeManager_RedoDone;
diff --git a/AudioCuesheetEditor/Shared/Layouts/BaseLocalizedLayoutComponentBase.cs b/AudioCuesheetEditor/Shared/Layouts/BaseLocalizedLayoutComponentBase.cs
deleted file mode 100644
index 63470c21..00000000
--- a/AudioCuesheetEditor/Shared/Layouts/BaseLocalizedLayoutComponentBase.cs
+++ /dev/null
@@ -1,59 +0,0 @@
-//This file is part of AudioCuesheetEditor.
-
-//AudioCuesheetEditor is free software: you can redistribute it and/or modify
-//it under the terms of the GNU General Public License as published by
-//the Free Software Foundation, either version 3 of the License, or
-//(at your option) any later version.
-
-//AudioCuesheetEditor is distributed in the hope that it will be useful,
-//but WITHOUT ANY WARRANTY; without even the implied warranty of
-//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-//GNU General Public License for more details.
-
-//You should have received a copy of the GNU General Public License
-//along with Foobar. If not, see
-//.
-using AudioCuesheetEditor.Services.UI;
-using Microsoft.AspNetCore.Components;
-
-namespace AudioCuesheetEditor.Shared.Layouts
-{
- public class BaseLocalizedLayoutComponentBase: LayoutComponentBase, IDisposable
- {
- private bool disposedValue;
-
- [Inject]
- protected LocalizationService LocalizationService { get; set; } = default!;
-
- protected override void OnInitialized()
- {
- base.OnInitialized();
- LocalizationService.LocalizationChanged += LocalizationService_LocalizationChanged;
- }
-
- void LocalizationService_LocalizationChanged(object? sender, EventArgs args)
- {
- StateHasChanged();
- }
-
- protected virtual void Dispose(bool disposing)
- {
- if (!disposedValue)
- {
- if (disposing)
- {
- LocalizationService.LocalizationChanged -= LocalizationService_LocalizationChanged;
- }
-
- disposedValue = true;
- }
- }
-
- public void Dispose()
- {
- // Ändern Sie diesen Code nicht. Fügen Sie Bereinigungscode in der Methode "Dispose(bool disposing)" ein.
- Dispose(disposing: true);
- GC.SuppressFinalize(this);
- }
- }
-}
diff --git a/AudioCuesheetEditor/Shared/Layouts/MainLayout.razor b/AudioCuesheetEditor/Shared/Layouts/MainLayout.razor
index ba89985e..2a9a319d 100644
--- a/AudioCuesheetEditor/Shared/Layouts/MainLayout.razor
+++ b/AudioCuesheetEditor/Shared/Layouts/MainLayout.razor
@@ -15,8 +15,7 @@ You should have received a copy of the GNU General Public License
along with Foobar. If not, see
.
-->
-
-@inherits BaseLocalizedLayoutComponentBase
+@inherits LayoutComponentBase
@inject NavigationManager _navigationManager
@inject IStringLocalizer _localizer
@@ -26,7 +25,7 @@ along with Foobar. If not, see
-
+
@Body
diff --git a/AudioCuesheetEditor/Shared/Layouts/MainLayoutWithoutMenu.razor b/AudioCuesheetEditor/Shared/Layouts/MainLayoutWithoutMenu.razor
index 8791ef1c..1fa5d25d 100644
--- a/AudioCuesheetEditor/Shared/Layouts/MainLayoutWithoutMenu.razor
+++ b/AudioCuesheetEditor/Shared/Layouts/MainLayoutWithoutMenu.razor
@@ -15,8 +15,7 @@ You should have received a copy of the GNU General Public License
along with Foobar. If not, see
.
-->
-
-@inherits BaseLocalizedLayoutComponentBase
+@inherits LayoutComponentBase
@inject NavigationManager _navigationManager
@inject IStringLocalizer _localizer
From 7d5a6be892c36c38f2d9fb50b0da26407fdabd1c Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Fri, 27 Feb 2026 11:03:20 +0100
Subject: [PATCH 18/48] Update AppBar.cs
---
AudioCuesheetEditor.End2EndTests/Models/AppBar.cs | 2 ++
1 file changed, 2 insertions(+)
diff --git a/AudioCuesheetEditor.End2EndTests/Models/AppBar.cs b/AudioCuesheetEditor.End2EndTests/Models/AppBar.cs
index 6129ffd9..69bd206c 100644
--- a/AudioCuesheetEditor.End2EndTests/Models/AppBar.cs
+++ b/AudioCuesheetEditor.End2EndTests/Models/AppBar.cs
@@ -48,6 +48,8 @@ internal async Task ChangeLanguageAsync(string language)
{
await _page.GetByRole(AriaRole.Button, new() { Name = "Change language" }).ClickAsync();
await _page.GetByText(language).ClickAsync();
+ await _page.WaitForLoadStateAsync(LoadState.NetworkIdle);
+ await _page.WaitForFunctionAsync(@"() => window.Blazor !== undefined");
}
internal async Task UndoAsync()
From ab4f9b5624b5d87bc843894ea00281594915a5f1 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Fri, 27 Feb 2026 11:45:49 +0100
Subject: [PATCH 19/48] fix tracks table
---
AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor | 3 ++-
AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor | 3 ++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
index d5e36555..3ad6d073 100644
--- a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
+++ b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
@@ -20,7 +20,7 @@ along with Foobar. If not, see
@inject AutocompleteManager _autocompleteManager
@inject ValidationService _validationService
-
@@ -34,6 +34,7 @@ along with Foobar. If not, see
@code {
+ //TODO: Check if ToStringFunc really gets a nullable or not
[Parameter]
[EditorRequired]
public Track Track { get; set; } = default!;
diff --git a/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor b/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor
index d97e43ab..0e8d5261 100644
--- a/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor
+++ b/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor
@@ -20,7 +20,7 @@ along with Foobar. If not, see
@inject AutocompleteManager _autocompleteManager
@inject ValidationService _validationService
-
@@ -34,6 +34,7 @@ along with Foobar. If not, see
@code {
+ //TODO: Check if ToStringFunc really gets a nullable or not
[Parameter]
[EditorRequired]
public Track Track { get; set; } = default!;
From a6143a6231ee1afb8a121930f7f14cfad0ca3412 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Fri, 27 Feb 2026 13:04:06 +0100
Subject: [PATCH 20/48] Update Track.cs
---
AudioCuesheetEditor/Model/AudioCuesheet/Track.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs b/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs
index 60e42688..09b511b7 100644
--- a/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs
+++ b/AudioCuesheetEditor/Model/AudioCuesheet/Track.cs
@@ -60,7 +60,7 @@ public Track() { }
/// Create object with copied values from input
///
/// Object to copy values from
- /// /// Copy cuesheet reference from track also?
+ /// Copy cuesheet reference from track also?
public Track(ITrack track, Boolean copyCuesheetReference = true) : this()
{
CopyValues(track, copyCuesheetReference);
From 74326626a4b8a6de11c9b271477ba600bd41f9b9 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Fri, 27 Feb 2026 15:03:04 +0100
Subject: [PATCH 21/48] fix tests
---
AudioCuesheetEditor.End2EndTests/MSTestSettings.cs | 2 +-
AudioCuesheetEditor.Tests/MSTestSettings.cs | 2 +-
AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor | 3 +--
AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor | 3 +--
4 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs b/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs
index aaf278c8..2492be6d 100644
--- a/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs
+++ b/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs
@@ -1 +1 @@
-[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
+[assembly: Parallelize()]
diff --git a/AudioCuesheetEditor.Tests/MSTestSettings.cs b/AudioCuesheetEditor.Tests/MSTestSettings.cs
index e466aa12..8a30f591 100644
--- a/AudioCuesheetEditor.Tests/MSTestSettings.cs
+++ b/AudioCuesheetEditor.Tests/MSTestSettings.cs
@@ -1,3 +1,3 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
-[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
+[assembly: Parallelize()]
diff --git a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
index 3ad6d073..154acdbd 100644
--- a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
+++ b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
@@ -20,7 +20,7 @@ along with Foobar. If not, see
@inject AutocompleteManager _autocompleteManager
@inject ValidationService _validationService
-
@@ -34,7 +34,6 @@ along with Foobar. If not, see
@code {
- //TODO: Check if ToStringFunc really gets a nullable or not
[Parameter]
[EditorRequired]
public Track Track { get; set; } = default!;
diff --git a/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor b/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor
index 0e8d5261..4d787e9c 100644
--- a/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor
+++ b/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor
@@ -20,7 +20,7 @@ along with Foobar. If not, see
@inject AutocompleteManager _autocompleteManager
@inject ValidationService _validationService
-
@@ -34,7 +34,6 @@ along with Foobar. If not, see
@code {
- //TODO: Check if ToStringFunc really gets a nullable or not
[Parameter]
[EditorRequired]
public Track Track { get; set; } = default!;
From f5f4ad67354d00633052bf33a68283082dc4b513 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Fri, 27 Feb 2026 15:11:19 +0100
Subject: [PATCH 22/48] reset to method level parlellization
---
AudioCuesheetEditor.End2EndTests/MSTestSettings.cs | 2 +-
AudioCuesheetEditor.Tests/MSTestSettings.cs | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs b/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs
index 2492be6d..aaf278c8 100644
--- a/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs
+++ b/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs
@@ -1 +1 @@
-[assembly: Parallelize()]
+[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
diff --git a/AudioCuesheetEditor.Tests/MSTestSettings.cs b/AudioCuesheetEditor.Tests/MSTestSettings.cs
index 8a30f591..e466aa12 100644
--- a/AudioCuesheetEditor.Tests/MSTestSettings.cs
+++ b/AudioCuesheetEditor.Tests/MSTestSettings.cs
@@ -1,3 +1,3 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
-[assembly: Parallelize()]
+[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
From fba8d10267a9a27d9d0ae84f0365f243ec968cf8 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Tue, 3 Mar 2026 10:19:21 +0100
Subject: [PATCH 23/48] Update MSTestSettings.cs
---
AudioCuesheetEditor.End2EndTests/MSTestSettings.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs b/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs
index aaf278c8..2492be6d 100644
--- a/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs
+++ b/AudioCuesheetEditor.End2EndTests/MSTestSettings.cs
@@ -1 +1 @@
-[assembly: Parallelize(Scope = ExecutionScope.MethodLevel)]
+[assembly: Parallelize()]
From f05f0ee1688243f949508c5a372dff2484de2180 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Tue, 3 Mar 2026 14:41:28 +0100
Subject: [PATCH 24/48] Update ArtistColumn.razor
---
AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
index 154acdbd..b82322b0 100644
--- a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
+++ b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
@@ -20,7 +20,7 @@ along with Foobar. If not, see
@inject AutocompleteManager _autocompleteManager
@inject ValidationService _validationService
-
From ae3f9fdc8182e8fd487ea38ec73ced31d6ba4135 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Tue, 3 Mar 2026 15:07:43 +0100
Subject: [PATCH 25/48] Update ArtistColumn.razor
---
AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
index b82322b0..154acdbd 100644
--- a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
+++ b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
@@ -20,7 +20,7 @@ along with Foobar. If not, see
@inject AutocompleteManager _autocompleteManager
@inject ValidationService _validationService
-
From 8130f2a59e051e40fbf30c6c85f0691e54bfd5c9 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Tue, 3 Mar 2026 15:31:47 +0100
Subject: [PATCH 26/48] fix autocomplete handling
---
.../Shared/TrackList/ArtistColumn.razor | 48 +++++++-----
.../Shared/TrackList/TitleColumn.razor | 74 +++++++++++--------
2 files changed, 71 insertions(+), 51 deletions(-)
diff --git a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
index 154acdbd..6e949688 100644
--- a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
+++ b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor
@@ -20,9 +20,9 @@ along with Foobar. If not, see
@inject AutocompleteManager _autocompleteManager
@inject ValidationService _validationService
-
+
@context.Name
@if (context.Disambiguation != null)
@@ -38,39 +38,49 @@ along with Foobar. If not, see
[EditorRequired]
public Track Track { get; set; } = default!;
- MusicBrainzArtist? autocompleteArtist;
- string? artist;
- Timer? debounceTimer;
+ MusicBrainzArtist? _autocompleteArtist;
+ string? _artist;
+ CancellationTokenSource? _cancellationTokenSource;
protected override void OnParametersSet()
{
base.OnParametersSet();
- autocompleteArtist = new() { Name = Track.Artist };
- artist = autocompleteArtist.Name;
+ SetComponentValues(new() { Name = Track.Artist });
}
- void ValueChanged(MusicBrainzArtist? newValue)
+ void SetComponentValues(MusicBrainzArtist? newValue)
{
- autocompleteArtist = newValue;
- artist = autocompleteArtist?.Name;
- StartChangeTimer();
+ if (newValue?.Name != _autocompleteArtist?.Name)
+ {
+ _autocompleteArtist = newValue;
+ _artist = _autocompleteArtist?.Name;
+ }
}
- void OnBlur(FocusEventArgs args)
+ void ValueChanged(MusicBrainzArtist? newValue)
{
+ SetComponentValues(newValue);
StartChangeTimer();
}
void StartChangeTimer()
{
- debounceTimer = new Timer(100);
- debounceTimer.Elapsed += ChangeArtist;
- debounceTimer.AutoReset = false;
- debounceTimer.Start();
+ _cancellationTokenSource?.Cancel();
+ _cancellationTokenSource = new CancellationTokenSource();
+
+ _ = DebounceAsync(_cancellationTokenSource.Token);
}
- void ChangeArtist(object? sender, System.Timers.ElapsedEventArgs e)
+ async Task DebounceAsync(CancellationToken token)
{
- Track.Artist = artist;
+ try
+ {
+ await Task.Delay(100, token);
+ Track.Artist = _artist;
+ }
+ catch (TaskCanceledException)
+ {
+ //Nothing to do here
+ }
}
}
diff --git a/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor b/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor
index 4d787e9c..68b190ff 100644
--- a/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor
+++ b/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor
@@ -21,8 +21,8 @@ along with Foobar. If not, see
@inject ValidationService _validationService
+ @bind-Text="_title" Value="_autocompleteTrack" ValueChanged="TitleSelected" ResetValueOnEmptyText Clearable ShowProgressIndicator
+ Validation="(string? newTitle) => _validationService.Validate(Track, nameof(Track.Title))" MaxItems="null" CoerceText="false" OnBlur="() => StartChangeTimer()">
@autocompleteContext.Title
@if (autocompleteContext.Disambiguation != null)
@@ -42,55 +42,65 @@ along with Foobar. If not, see
[EditorRequired]
public ViewMode CurrentViewMode { get; set; }
- MusicBrainzTrack? autocompleteTrack;
- string? title;
- Timer? debounceTimer;
+ MusicBrainzTrack? _autocompleteTrack;
+ string? _title;
+ CancellationTokenSource? _cancellationTokenSource;
protected override void OnParametersSet()
{
base.OnParametersSet();
- autocompleteTrack = new() { Artist = Track.Artist, Title = Track.Title };
- title = autocompleteTrack?.Title;
+ SetComponentValues(new() { Artist = Track.Artist, Title = Track.Title });
}
- void TitleSelected(MusicBrainzTrack? newValue)
+ void SetComponentValues(MusicBrainzTrack? newValue)
{
- autocompleteTrack = newValue;
- title = autocompleteTrack?.Title;
- StartChangeTimer();
+ if ((newValue?.Artist != _autocompleteTrack?.Artist) || (newValue?.Title != _autocompleteTrack?.Title))
+ {
+ _autocompleteTrack = newValue;
+ _title = _autocompleteTrack?.Title;
+ }
}
- void OnBlur(FocusEventArgs args)
+ void TitleSelected(MusicBrainzTrack? newValue)
{
+ SetComponentValues(newValue);
StartChangeTimer();
}
void StartChangeTimer()
{
- debounceTimer = new Timer(100);
- debounceTimer.Elapsed += ChangeTitle;
- debounceTimer.AutoReset = false;
- debounceTimer.Start();
+ _cancellationTokenSource?.Cancel();
+ _cancellationTokenSource = new CancellationTokenSource();
+
+ _ = DebounceAsync(_cancellationTokenSource.Token);
}
- void ChangeTitle(object? sender, System.Timers.ElapsedEventArgs e)
+ async Task DebounceAsync(CancellationToken token)
{
- base.TraceChangeManager.BulkEdit = true;
- Track.Title = title;
- switch (CurrentViewMode)
+ try
+ {
+ await Task.Delay(100, token);
+ base.TraceChangeManager.BulkEdit = true;
+ Track.Title = _title;
+ switch (CurrentViewMode)
+ {
+ case ViewMode.DetailView:
+ case ViewMode.ImportView:
+ if ((String.IsNullOrEmpty(Track.Artist)) && (String.IsNullOrEmpty(_autocompleteTrack?.Artist) == false))
+ {
+ Track.Artist = _autocompleteTrack.Artist;
+ }
+ if ((Track.Length.HasValue == false) && (_autocompleteTrack?.Length.HasValue == true))
+ {
+ Track.Length = _autocompleteTrack?.Length;
+ }
+ break;
+ }
+ base.TraceChangeManager.BulkEdit = false;
+ }
+ catch (TaskCanceledException)
{
- case ViewMode.DetailView:
- case ViewMode.ImportView:
- if ((String.IsNullOrEmpty(Track.Artist)) && (String.IsNullOrEmpty(autocompleteTrack?.Artist) == false))
- {
- Track.Artist = autocompleteTrack.Artist;
- }
- if ((Track.Length.HasValue == false) && (autocompleteTrack?.Length.HasValue == true))
- {
- Track.Length = autocompleteTrack?.Length;
- }
- break;
+ //Nothing to do here
}
- base.TraceChangeManager.BulkEdit = false;
}
}
From 40f1b6744342fe21f9670733748fc12322b31aa0 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Wed, 4 Mar 2026 09:12:30 +0100
Subject: [PATCH 27/48] fix browser issues
---
.../Models/AppBar.cs | 16 +++++-----------
.../Tests/Desktop/ExportTest.cs | 2 +-
.../Tests/Smartphone/ExportTestSmartphone.cs | 2 +-
AudioCuesheetEditor/Shared/AppBar.razor | 2 +-
.../Shared/Audio/AudioPlayer.razor | 10 +++++-----
.../Shared/Cuesheet/EditSections.razor | 2 +-
.../Shared/Dialogs/ConfirmationDialog.razor | 4 ++--
.../Shared/Inputs/FileInput.razor | 10 +++++-----
8 files changed, 21 insertions(+), 27 deletions(-)
diff --git a/AudioCuesheetEditor.End2EndTests/Models/AppBar.cs b/AudioCuesheetEditor.End2EndTests/Models/AppBar.cs
index 69bd206c..ee614bf3 100644
--- a/AudioCuesheetEditor.End2EndTests/Models/AppBar.cs
+++ b/AudioCuesheetEditor.End2EndTests/Models/AppBar.cs
@@ -18,13 +18,13 @@
namespace AudioCuesheetEditor.End2EndTests.Models
{
- partial class AppBar
+ partial class AppBar(IPage page)
{
[GeneratedRegex("^Open$")]
private static partial Regex OpenRegex();
- private readonly IPage _page;
- private readonly ILocator _menuButton;
+ private readonly IPage _page = page;
+ internal ILocator MenuButton => _page.GetByRole(AriaRole.Toolbar).GetByRole(AriaRole.Button, new() { Name = "More" });
internal ILocator UndoButton => _page.GetByRole(AriaRole.Button, new() { Name = "undo" });
@@ -32,15 +32,9 @@ partial class AppBar
internal ILocator HomeButton => _page.Locator(".mud-button-root").First;
- internal AppBar(IPage page)
- {
- _page = page;
- _menuButton = _page.GetByRole(AriaRole.Button, new() { Name = "More" });
- }
-
internal async Task OpenSettingsAsync()
{
- await _menuButton.ClickAsync();
+ await MenuButton.ClickAsync();
await _page.GetByText("Settings").ClickAsync();
}
@@ -83,7 +77,7 @@ internal async Task OpenExportDialogAsync(string exportType, string fileMenuName
internal async Task OpenDisplayHotkeysAsync()
{
- await _page.GetByRole(AriaRole.Button, new() { Name = "More" }).ClickAsync();
+ await MenuButton.ClickAsync();
await _page.GetByText("Hotkeys").ClickAsync();
}
}
diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs
index 85555e5b..58d53768 100644
--- a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs
+++ b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs
@@ -83,7 +83,7 @@ public async Task DownloadText_GeneratesTextFile_WhenCuesheetIsValidAsync()
await detailView.AudiofileInput.SetInputFilesAsync("Kalimba.mp3");
await detailView.EditTrackAsync("Track Artist 1", "Track Title 1");
await bar.OpenExportDialogAsync("Textfile");
- await TestPage.GetByRole(AriaRole.Button, new() { Name = "Next" }).ClickAsync();
+ await TestPage.GetByRole(AriaRole.Button, new() { Name = "Next", Exact = true }).ClickAsync();
var downloadTask = TestPage.WaitForDownloadAsync();
await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download-YouTube.txt" }).ClickAsync();
var download = await downloadTask;
diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTestSmartphone.cs b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTestSmartphone.cs
index 385e3aa3..6bee002f 100644
--- a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTestSmartphone.cs
+++ b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTestSmartphone.cs
@@ -83,7 +83,7 @@ public async Task DownloadText_GeneratesTextFile_WhenCuesheetIsValidAsync()
await detailView.AudiofileInput.SetInputFilesAsync("Kalimba.mp3");
await detailView.EditTrackAsync("Track Artist 1", "Track Title 1");
await bar.OpenExportDialogAsync("Textfile");
- await TestPage.GetByRole(AriaRole.Button, new() { Name = "Next" }).ClickAsync();
+ await TestPage.GetByRole(AriaRole.Button, new() { Name = "Next", Exact = true }).ClickAsync();
var downloadTask = TestPage.WaitForDownloadAsync();
await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download-YouTube.txt" }).ClickAsync();
var download = await downloadTask;
diff --git a/AudioCuesheetEditor/Shared/AppBar.razor b/AudioCuesheetEditor/Shared/AppBar.razor
index 771d40f1..746f5c60 100644
--- a/AudioCuesheetEditor/Shared/AppBar.razor
+++ b/AudioCuesheetEditor/Shared/AppBar.razor
@@ -27,7 +27,7 @@ along with Foobar. If not, see
@inject ImportManager _importManager
-
+
AudioCuesheetEditor
diff --git a/AudioCuesheetEditor/Shared/Audio/AudioPlayer.razor b/AudioCuesheetEditor/Shared/Audio/AudioPlayer.razor
index 11224223..5533938b 100644
--- a/AudioCuesheetEditor/Shared/Audio/AudioPlayer.razor
+++ b/AudioCuesheetEditor/Shared/Audio/AudioPlayer.razor
@@ -38,7 +38,7 @@ along with Foobar. If not, see
{
@String.Format("--{0}--{1}--", CultureInfo.CurrentCulture.DateTimeFormat.TimeSeparator, CultureInfo.CurrentCulture.DateTimeFormat.TimeSeparator)
}
-
+
@GetSliderTimeValue()
@@ -54,17 +54,17 @@ along with Foobar. If not, see
-
+
-
+
-
+
-
+
diff --git a/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor b/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor
index fa21e07c..d1abd5cb 100644
--- a/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor
+++ b/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor
@@ -26,7 +26,7 @@ along with Foobar. If not, see
-
+
diff --git a/AudioCuesheetEditor/Shared/Dialogs/ConfirmationDialog.razor b/AudioCuesheetEditor/Shared/Dialogs/ConfirmationDialog.razor
index 4aafb337..98b7d01a 100644
--- a/AudioCuesheetEditor/Shared/Dialogs/ConfirmationDialog.razor
+++ b/AudioCuesheetEditor/Shared/Dialogs/ConfirmationDialog.razor
@@ -24,8 +24,8 @@ along with Foobar. If not, see
@ConfirmText
- @_localizer["Yes"]
- @_localizer["No"]
+ @_localizer["Yes"]
+ @_localizer["No"]
diff --git a/AudioCuesheetEditor/Shared/Inputs/FileInput.razor b/AudioCuesheetEditor/Shared/Inputs/FileInput.razor
index 91ebeb91..65769f6c 100644
--- a/AudioCuesheetEditor/Shared/Inputs/FileInput.razor
+++ b/AudioCuesheetEditor/Shared/Inputs/FileInput.razor
@@ -21,19 +21,19 @@ along with Foobar. If not, see
+ Accept="@Filter" aria-label="Upload file">
- @_localizer["Search"]
+ @_localizer["Search"]
-
+
@if (DisplayMenu)
{
-
- @_localizer["Rename file"]
+
+ @_localizer["Rename file"]
}
From fbb3a92aebcbc83cd3d93d1758fcbb7b793e16fd Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Wed, 4 Mar 2026 13:29:41 +0100
Subject: [PATCH 28/48] Update Readme.md
---
Readme.md | 22 +++++++++++++---------
1 file changed, 13 insertions(+), 9 deletions(-)
diff --git a/Readme.md b/Readme.md
index 23511888..2a69f968 100644
--- a/Readme.md
+++ b/Readme.md
@@ -1,36 +1,40 @@
-## Introduction
+# Introduction
-### What is AudioCuesheetEditor?
+## What is AudioCuesheetEditor?
AudioCuesheetEditor is a Blazor based web port of AudioCuesheetEditor (https://sourceforge.net/projects/audiocuesheet/).
Basically it is a program for handling audio cuesheet files and everything around.
-### Description
+## Description
AudioCuesheetEditor is a Blazor based web application for writing audio cuesheets. There is much validation that helps the user to write a valid cuesheet. You can import external data (like text files, xml files, etc.) and analyse them directly in GUI. There are also much export variations like CSV, but you can customize export freely.
-## Usage
+# Usage
Simply open the link https://audiocuesheeteditor.netlify.app/ on any browser
-## Environments
+# Environments
-### Production
+## Production
[](https://github.com/NeoCoderMatrix86/AudioCuesheetEditor/actions/workflows/build_pipeline.yml)
The current stable version can be found here: https://audiocuesheeteditor.netlify.app/
-### Preview
+## Preview
[](https://github.com/NeoCoderMatrix86/AudioCuesheetEditor/actions/workflows/build_pipeline.yml)
The next release candidate version can be found here: https://preview-audiocuesheeteditor.netlify.app/
-## Contributing
+# Contributing
You can contribute to the project by opening up issues or adding feature requests. To do so, you simply open https://github.com/NeoCoderMatrix86/AudioCuesheetEditor/issues and add a new issue. If you want, you can also contribute by developing a feature or fixing a bug. More can be found here: https://github.com/NeoCoderMatrix86/AudioCuesheetEditor/blob/master/CONTRIBUTING.md
-## License
+## Requirements
+
+This application is build using Blazor WASM Standalone and needs .NET 10 to run. Tests are running using MSTest and Playwright.
+
+# License
GNU GENERAL PUBLIC LICENSE Version 3
From d535581cb21b08dde4a6321dc3ae7a558fdb6321 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Wed, 4 Mar 2026 13:33:43 +0100
Subject: [PATCH 29/48] Update AudioCuesheetEditor.csproj
---
AudioCuesheetEditor/AudioCuesheetEditor.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AudioCuesheetEditor/AudioCuesheetEditor.csproj b/AudioCuesheetEditor/AudioCuesheetEditor.csproj
index efe02493..d1d88f83 100644
--- a/AudioCuesheetEditor/AudioCuesheetEditor.csproj
+++ b/AudioCuesheetEditor/AudioCuesheetEditor.csproj
@@ -7,7 +7,7 @@
enable
https://github.com/NeoCoderMatrix86/AudioCuesheetEditor
3.0
- 10.4.0
+ 11.0.0
false
true
true
From 5ca3079512645c8a277fb59457b3ade106db08ec Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Wed, 4 Mar 2026 13:40:00 +0100
Subject: [PATCH 30/48] Update AudioCuesheetEditor.csproj
---
AudioCuesheetEditor/AudioCuesheetEditor.csproj | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AudioCuesheetEditor/AudioCuesheetEditor.csproj b/AudioCuesheetEditor/AudioCuesheetEditor.csproj
index d1d88f83..d7bb3822 100644
--- a/AudioCuesheetEditor/AudioCuesheetEditor.csproj
+++ b/AudioCuesheetEditor/AudioCuesheetEditor.csproj
@@ -26,7 +26,7 @@
-
+
From 7436a633e67a761ed2aa2413deb9557dbfa8b3ab Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Wed, 4 Mar 2026 16:07:01 +0100
Subject: [PATCH 31/48] update libraries
---
.../AudioCuesheetEditor.csproj | 2 +-
AudioCuesheetEditor/Shared/AppBar.razor | 5 +++-
.../Dialogs/EditMultipleTracksModal.razor | 20 ++++++-------
.../Shared/Dialogs/SettingsDialog.razor | 29 +++++++++----------
.../Shared/Import/Importprofiles.razor | 27 +++++++++++------
.../Shared/Inputs/FileDropOverlay.razor | 1 +
.../Shared/Inputs/FileInput.razor | 9 +++---
.../Shared/Inputs/TextField.razor | 7 +++--
.../Shared/TrackList/TrackList.razor | 4 +--
AudioCuesheetEditor/wwwroot/css/app.css | 4 +++
10 files changed, 63 insertions(+), 45 deletions(-)
diff --git a/AudioCuesheetEditor/AudioCuesheetEditor.csproj b/AudioCuesheetEditor/AudioCuesheetEditor.csproj
index d7bb3822..9dd43125 100644
--- a/AudioCuesheetEditor/AudioCuesheetEditor.csproj
+++ b/AudioCuesheetEditor/AudioCuesheetEditor.csproj
@@ -29,7 +29,7 @@
-
+
diff --git a/AudioCuesheetEditor/Shared/AppBar.razor b/AudioCuesheetEditor/Shared/AppBar.razor
index 746f5c60..2a5d3638 100644
--- a/AudioCuesheetEditor/Shared/AppBar.razor
+++ b/AudioCuesheetEditor/Shared/AppBar.razor
@@ -46,7 +46,10 @@ along with Foobar. If not, see
@if (DisplayFileMenu)
{
-
+
+
+
+
@_localizer["Open"]
diff --git a/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.razor b/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.razor
index 9c8a87f7..e029d6f8 100644
--- a/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.razor
+++ b/AudioCuesheetEditor/Shared/Dialogs/EditMultipleTracksModal.razor
@@ -33,7 +33,7 @@ along with Foobar. If not, see
@_localizer["Value"]
-
+
@@ -41,7 +41,7 @@ along with Foobar. If not, see
-
+
@@ -54,7 +54,7 @@ along with Foobar. If not, see
-
+
@@ -82,7 +82,7 @@ along with Foobar. If not, see
-
+
@@ -111,7 +111,7 @@ along with Foobar. If not, see
-
+
@@ -125,7 +125,7 @@ along with Foobar. If not, see
-
+
@@ -139,7 +139,7 @@ along with Foobar. If not, see
-
+
@@ -153,7 +153,7 @@ along with Foobar. If not, see
-
+
@@ -170,7 +170,7 @@ along with Foobar. If not, see
-
+
@@ -184,7 +184,7 @@ along with Foobar. If not, see
-
+
diff --git a/AudioCuesheetEditor/Shared/Dialogs/SettingsDialog.razor b/AudioCuesheetEditor/Shared/Dialogs/SettingsDialog.razor
index b512dbe6..db6755b5 100644
--- a/AudioCuesheetEditor/Shared/Dialogs/SettingsDialog.razor
+++ b/AudioCuesheetEditor/Shared/Dialogs/SettingsDialog.razor
@@ -23,21 +23,21 @@ along with Foobar. If not, see
@_localizer["Input"]
-
-
-
+
+
@foreach(var scheme in TimeSpanFormat.AvailableTimespanScheme)
{
@_localizer[scheme]
}
@_localizer["Display"]
-
-
+
@foreach (var level in Enum.GetValues())
{
@level
@@ -47,14 +47,13 @@ along with Foobar. If not, see
@code {
- MudTextField? timeInputFormatTextField;
- ApplicationOptions? applicationOptions;
- MudMenu? timespanFormatMenu;
+ ApplicationOptions? _applicationOptions;
+ MudMenu? _timespanFormatMenu;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
- applicationOptions = await LocalStorageOptionsProvider.GetOptionsAsync();
+ _applicationOptions = await LocalStorageOptionsProvider.GetOptionsAsync();
LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved;
}
@@ -66,7 +65,7 @@ along with Foobar. If not, see
async Task TimeInputFormatChangedAsync(string newValue)
{
- TimeSpanFormat? timeSpanFormat = applicationOptions?.TimeSpanFormat;
+ TimeSpanFormat? timeSpanFormat = _applicationOptions?.TimeSpanFormat;
if (string.IsNullOrEmpty(newValue))
{
timeSpanFormat = null;
@@ -87,9 +86,9 @@ along with Foobar. If not, see
await LocalStorageOptionsProvider.SaveOptionsValueAsync(x => x.DisplayTimeSpanFormat, newValue);
}
- void AppendPlaceholderToTimeInputFormatTextField(string placeholder)
+ async Task AppendPlaceholderToTimeInputFormatTextField(string placeholder)
{
- timeInputFormatTextField?.SetText($"{timeInputFormatTextField.Text}{placeholder}");
+ await TimeInputFormatChangedAsync($"{_applicationOptions?.TimeSpanFormat?.Scheme}{placeholder}");
}
async Task LogLevelChanged(LogLevel logLevel)
@@ -101,7 +100,7 @@ along with Foobar. If not, see
{
if (option is ApplicationOptions applicationOption)
{
- applicationOptions = applicationOption;
+ _applicationOptions = applicationOption;
StateHasChanged();
}
}
diff --git a/AudioCuesheetEditor/Shared/Import/Importprofiles.razor b/AudioCuesheetEditor/Shared/Import/Importprofiles.razor
index ca9c9da3..ffbd2063 100644
--- a/AudioCuesheetEditor/Shared/Import/Importprofiles.razor
+++ b/AudioCuesheetEditor/Shared/Import/Importprofiles.razor
@@ -51,7 +51,7 @@ along with Foobar. If not, see
Validation="(string newValue) => _validationService.Validate(importOptions.SelectedImportProfile, nameof(Importprofile.Name))"
Label="@_localizer["Profile name"]" Placeholder="@_localizer["Enter the name for this profile here"]" Variant="Variant.Outlined" />
-
@foreach (var scheme in Importprofile.AvailableSchemeCuesheet)
{
- @_localizer[scheme]
+ @_localizer[scheme]
}
-
@foreach (var scheme in Importprofile.AvailableSchemesTrack)
{
- @_localizer[scheme]
+ @_localizer[scheme]
}
-
@foreach (var scheme in TimeSpanFormat.AvailableTimespanScheme)
{
- @_localizer[scheme]
+ @_localizer[scheme]
}
@@ -91,7 +91,6 @@ along with Foobar. If not, see
@code {
ImportOptions? importOptions;
MudMenu? schemeCuesheetMenu, schemeTracksMenu, timeSpanFormatMenu;
- MudTextField? schemeCuesheetTextField, schemeTracksTextField, timeSpanFormatTextField;
protected override async Task OnInitializedAsync()
{
@@ -106,9 +105,19 @@ along with Foobar. If not, see
base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved;
}
- void AppendPlaceholderToTextField(MudTextField? mudTextField, string placeholder)
+ async Task AppendPlaceholderToSchemeCuesheet(string placeholder)
{
- mudTextField?.SetText($"{mudTextField.Text}{placeholder}");
+ await SchemeCuesheetChangedAsync($"{importOptions?.SelectedImportProfile?.SchemeCuesheet}{placeholder}");
+ }
+
+ async Task AppendPlaceholderToSchemeTracks(string placeholder)
+ {
+ await SchemeCuesheetChangedAsync($"{importOptions?.SelectedImportProfile?.SchemeTracks}{placeholder}");
+ }
+
+ async Task AppendPlaceholderToImportTimeInputFormat(string placeholder)
+ {
+ await SchemeCuesheetChangedAsync($"{importOptions?.SelectedImportProfile?.TimeSpanFormat?.Scheme}{placeholder}");
}
async Task SelectedImportProfileChangedAsync(Importprofile? newSelectedProfile)
diff --git a/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor b/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor
index f36353d9..42c3a9a7 100644
--- a/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor
+++ b/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor
@@ -38,6 +38,7 @@ along with Foobar. If not, see
@code {
+ //TODO: Get this component working with the new MudFileUpload
private DotNetObjectReference? _dotNetRef;
private bool _showOverlay;
private bool _disposed;
diff --git a/AudioCuesheetEditor/Shared/Inputs/FileInput.razor b/AudioCuesheetEditor/Shared/Inputs/FileInput.razor
index 65769f6c..860ee8d8 100644
--- a/AudioCuesheetEditor/Shared/Inputs/FileInput.razor
+++ b/AudioCuesheetEditor/Shared/Inputs/FileInput.razor
@@ -20,11 +20,12 @@ along with Foobar. If not, see
@inject IStringLocalizer _localizer
-
-
-
-
+
+ fileUpload.OpenFilePickerAsync()" />
+
+
@_localizer["Search"]
diff --git a/AudioCuesheetEditor/Shared/Inputs/TextField.razor b/AudioCuesheetEditor/Shared/Inputs/TextField.razor
index 0dd4df3e..dfc5d589 100644
--- a/AudioCuesheetEditor/Shared/Inputs/TextField.razor
+++ b/AudioCuesheetEditor/Shared/Inputs/TextField.razor
@@ -24,9 +24,10 @@ along with Foobar. If not, see
-
- @_localizer["Upload file"]
-
+
+ @_localizer["Upload file"]
+
+
diff --git a/AudioCuesheetEditor/Shared/TrackList/TrackList.razor b/AudioCuesheetEditor/Shared/TrackList/TrackList.razor
index 6475cdfa..a445f42b 100644
--- a/AudioCuesheetEditor/Shared/TrackList/TrackList.razor
+++ b/AudioCuesheetEditor/Shared/TrackList/TrackList.razor
@@ -298,7 +298,7 @@ along with Foobar. If not, see
track.Position = newPosition;
if (form != null)
{
- await form.Validate();
+ await form.ValidateAsync();
}
}
@@ -307,7 +307,7 @@ along with Foobar. If not, see
await _applicationOptionsTimeSpanParser.TimespanTextChanged
- @_localizer["Delete selected export profile"]
+ @_localizer["Delete selected export profile"]
-
+
-
-
+
-
+
-
+ Validation="(string? newValue) => _validationService.Validate(_exportOptions.SelectedExportProfile, nameof(Exportprofile.SchemeHead))"
+ Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Outlined.AddBox" OnAdornmentClick="(args) => AdornmentClickAsync(_schemeHeadMenu, args)" />
+
@foreach (var placeholder in Exportprofile.AvailableCuesheetSchemes)
{
@_localizer[placeholder.Key]
}
-
-
+ Validation="(string? newValue) => _validationService.Validate(_exportOptions.SelectedExportProfile, nameof(Exportprofile.SchemeTracks))"
+ Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Outlined.AddBox" OnAdornmentClick="(args) => AdornmentClickAsync(_schemeTracksMenu, args)" />
+
@foreach (var placeholder in Exportprofile.AvailableTrackSchemes)
{
@_localizer[placeholder.Key]
}
-
-
+ Validation="(string? newValue) => _validationService.Validate(_exportOptions.SelectedExportProfile, nameof(Exportprofile.SchemeFooter))"
+ Adornment="Adornment.End" AdornmentIcon="@Icons.Material.Outlined.AddBox" OnAdornmentClick="(args) => AdornmentClickAsync(_schemeFooterMenu, args)" />
+
@foreach (var placeholder in Exportprofile.AvailableCuesheetSchemes)
{
@_localizer[placeholder.Key]
@@ -121,14 +121,14 @@ along with Foobar. If not, see
- @if (exportFiles.Count() == 1)
+ @if (_exportFiles.Count() == 1)
{
- var exportFile = exportFiles.Single();
+ var exportFile = _exportFiles.Single();
}
else
{
-
+
@_localizer["Name"]
@_localizer["Begin"]
@@ -140,9 +140,18 @@ along with Foobar. If not, see
@context.Begin
@context.End
@{
- var ariaLabel = $"Download-{context.Name}";
+ var ariaLabelDownload = $"Download-{context.Name}";
+ var ariaLabelDialog = $"Dialog-{context.Name}";
+ var dialogVisible = _displayContentDialogVisible.GetValueOrDefault(context);
+
+ @_localizer["Display content"]
+
+
+
+
-
+
+
}
@@ -155,11 +164,13 @@ along with Foobar. If not, see
}
@code {
- //TODO: Display content for multiple files
- ExportOptions? exportOptions;
- IEnumerable exportFiles = [];
- Boolean configureExportCompleted = false;
- MudMenu? schemeHeadMenu, schemeTracksMenu, schemeFooterMenu;
+ ExportOptions? _exportOptions;
+ IEnumerable _exportFiles = [];
+ Boolean _configureExportCompleted = false;
+ MudMenu? _schemeHeadMenu, _schemeTracksMenu, _schemeFooterMenu;
+ Dictionary _displayContentDialogVisible = new();
+
+ private readonly DialogOptions _displayContentDialogOptions = new() { CloseButton = true, CloseOnEscapeKey = true, FullWidth = true, MaxWidth = MaxWidth.ExtraExtraLarge };
protected override void Dispose(bool disposing)
{
@@ -171,43 +182,43 @@ along with Foobar. If not, see
{
await base.OnInitializedAsync();
LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved;
- exportOptions = await LocalStorageOptionsProvider.GetOptionsAsync();
+ _exportOptions = await LocalStorageOptionsProvider.GetOptionsAsync();
}
void LocalStorageOptionsProvider_OptionSaved(object? sender, IOptions option)
{
if (option is ExportOptions exportOption)
{
- exportOptions = exportOption;
+ _exportOptions = exportOption;
StateHasChanged();
}
}
async Task AddClick()
{
- if (exportOptions != null)
+ if (_exportOptions != null)
{
var newProfile = new Exportprofile();
- exportOptions.ExportProfiles.Add(newProfile);
- exportOptions.SelectedExportProfile = newProfile;
- await LocalStorageOptionsProvider.SaveOptionsAsync(exportOptions);
+ _exportOptions.ExportProfiles.Add(newProfile);
+ _exportOptions.SelectedExportProfile = newProfile;
+ await LocalStorageOptionsProvider.SaveOptionsAsync(_exportOptions);
}
}
async Task DeleteClick()
{
- if (exportOptions?.SelectedExportProfile != null)
+ if (_exportOptions?.SelectedExportProfile != null)
{
- exportOptions.ExportProfiles.Remove(exportOptions.SelectedExportProfile);
- exportOptions.SelectedExportProfile = exportOptions.ExportProfiles.LastOrDefault();
- await LocalStorageOptionsProvider.SaveOptionsAsync(exportOptions);
+ _exportOptions.ExportProfiles.Remove(_exportOptions.SelectedExportProfile);
+ _exportOptions.SelectedExportProfile = _exportOptions.ExportProfiles.LastOrDefault();
+ await LocalStorageOptionsProvider.SaveOptionsAsync(_exportOptions);
}
}
String? GetGenerationValidationMessages()
{
String? validationErrorMessage = null;
- var messages = _exportfileGenerator.CanGenerateExportfiles(exportOptions?.SelectedExportProfile);
+ var messages = _exportfileGenerator.CanGenerateExportfiles(_exportOptions?.SelectedExportProfile);
if (messages.Count() > 0)
{
validationErrorMessage = String.Join("
", messages.Select(x => x.GetMessageLocalized(_validationMessageLocalizer)));
@@ -228,8 +239,8 @@ along with Foobar. If not, see
{
if (newIndex == 1)
{
- exportFiles = _exportfileGenerator.GenerateExportfiles(exportOptions?.SelectedExportProfile);
- configureExportCompleted = exportFiles.Any();
+ _exportFiles = _exportfileGenerator.GenerateExportfiles(_exportOptions?.SelectedExportProfile);
+ _configureExportCompleted = _exportFiles.Any();
}
}
diff --git a/AudioCuesheetEditor/Shared/Dialogs/GenerateExportDialog.resx b/AudioCuesheetEditor/Shared/Dialogs/GenerateExportDialog.resx
index 1a4e41a3..b44b5523 100644
--- a/AudioCuesheetEditor/Shared/Dialogs/GenerateExportDialog.resx
+++ b/AudioCuesheetEditor/Shared/Dialogs/GenerateExportDialog.resx
@@ -216,4 +216,7 @@
PostGap
+
+ Display content
+
\ No newline at end of file
diff --git a/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor b/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor
index abd7eed1..78fd8a21 100644
--- a/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor
+++ b/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor
@@ -22,10 +22,10 @@ along with Foobar. If not, see
@inject IJSRuntime _jsRuntime
-
+
@_localizer["Copy"]
-
+
@@ -34,17 +34,17 @@ along with Foobar. If not, see
[Parameter]
[EditorRequired]
- public Exportfile Exportfile { get; set; } = default!;
+ public Exportfile? Exportfile { get; set; }
protected override void OnParametersSet()
{
base.OnParametersSet();
- _lineCount = Exportfile.Content?.Split(Environment.NewLine).Length ?? 1;
+ _lineCount = Exportfile?.Content?.Split(Environment.NewLine).Length ?? 1;
}
async Task CopyClipboardClicked()
{
//TODO: feedback for user
- await _jsRuntime.InvokeVoidAsync("navigator.clipboard.writeText", Exportfile.Content);
+ await _jsRuntime.InvokeVoidAsync("navigator.clipboard.writeText", Exportfile?.Content);
}
}
From 432894cde586ac0c88e08456374e0c568812d389 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Thu, 5 Mar 2026 13:43:00 +0100
Subject: [PATCH 42/48] add feedback for user
---
.../Shared/Export/DisplayExportfileContent.de.resx | 3 +++
.../Shared/Export/DisplayExportfileContent.razor | 9 ++++++++-
.../Shared/Export/DisplayExportfileContent.resx | 3 +++
3 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.de.resx b/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.de.resx
index fcd6beb0..19803525 100644
--- a/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.de.resx
+++ b/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.de.resx
@@ -117,6 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ In Zwischenablage kopiert
+
Kopieren
diff --git a/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor b/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor
index 78fd8a21..114bbcbe 100644
--- a/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor
+++ b/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor
@@ -20,6 +20,7 @@ along with Foobar. If not, see
@inject IStringLocalizer _localizer
@inject IBlazorDownloadFileService _blazorDownloadFileService
@inject IJSRuntime _jsRuntime
+@inject ISnackbar _snackbar
@@ -44,7 +45,13 @@ along with Foobar. If not, see
async Task CopyClipboardClicked()
{
- //TODO: feedback for user
await _jsRuntime.InvokeVoidAsync("navigator.clipboard.writeText", Exportfile?.Content);
+ _snackbar.Add(_localizer["Copied to clipboard"], Severity.Info, config =>
+ {
+ config.ShowTransitionDuration = 500;
+ config.VisibleStateDuration = 1000;
+ config.HideTransitionDuration = 500;
+ config.ShowCloseIcon = false;
+ });
}
}
diff --git a/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.resx b/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.resx
index fa348179..2af11de2 100644
--- a/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.resx
+++ b/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.resx
@@ -117,6 +117,9 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+ Copied to clipboard
+
Copy
From 1e3e089103b6debf06d9b3d0e3ffb25fb42737f7 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Thu, 5 Mar 2026 13:56:24 +0100
Subject: [PATCH 43/48] fix tests
---
.../Tests/Desktop/ExportTest.cs | 2 +-
.../Tests/Smartphone/ExportTestSmartphone.cs | 2 +-
.../Services/IO/CuesheetExportServiceTests.cs | 6 ++----
.../Services/IO/ExportfileGeneratorTests.cs | 6 ++----
.../Shared/Dialogs/GenerateCuesheetDialog.razor | 2 +-
.../Shared/Export/DisplayExportfileContent.razor | 4 ++--
6 files changed, 9 insertions(+), 13 deletions(-)
diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs
index 03108b43..e2cb6937 100644
--- a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs
+++ b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs
@@ -85,7 +85,7 @@ public async Task DownloadText_GeneratesTextFile_WhenCuesheetIsValidAsync()
await bar.OpenExportDialogAsync("Textfile");
await TestPage.GetByRole(AriaRole.Button, new() { Name = "Next", Exact = true }).ClickAsync();
var downloadTask = TestPage.WaitForDownloadAsync();
- await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download-YouTube.txt" }).ClickAsync();
+ await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download" }).ClickAsync();
var download = await downloadTask;
using var stream = await download.CreateReadStreamAsync();
using var reader = new StreamReader(stream);
diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTestSmartphone.cs b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTestSmartphone.cs
index eafe2b1f..61cdc3fd 100644
--- a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTestSmartphone.cs
+++ b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTestSmartphone.cs
@@ -85,7 +85,7 @@ public async Task DownloadText_GeneratesTextFile_WhenCuesheetIsValidAsync()
await bar.OpenExportDialogAsync("Textfile");
await TestPage.GetByRole(AriaRole.Button, new() { Name = "Next", Exact = true }).ClickAsync();
var downloadTask = TestPage.WaitForDownloadAsync();
- await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download-YouTube.txt" }).ClickAsync();
+ await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download" }).ClickAsync();
var download = await downloadTask;
using var stream = await download.CreateReadStreamAsync();
using var reader = new StreamReader(stream);
diff --git a/AudioCuesheetEditor.Tests/Services/IO/CuesheetExportServiceTests.cs b/AudioCuesheetEditor.Tests/Services/IO/CuesheetExportServiceTests.cs
index 968fc387..bfa05bc5 100644
--- a/AudioCuesheetEditor.Tests/Services/IO/CuesheetExportServiceTests.cs
+++ b/AudioCuesheetEditor.Tests/Services/IO/CuesheetExportServiceTests.cs
@@ -115,7 +115,6 @@ public void GenerateExportfiles_WithoutSections_ReturnsExportFile()
Assert.AreEqual(new TimeSpan(0, 8, 32), result.First().End);
var content = result.First().Content;
Assert.IsNotNull(content);
- var contentString = Encoding.UTF8.GetString(content);
Assert.AreEqual(@"TITLE ""Test title cuesheet""
PERFORMER ""Test artist cuesheet""
FILE ""Test audiofile.mp3"" MP3
@@ -127,7 +126,7 @@ TRACK 02 AUDIO
TITLE ""Test title 2""
PERFORMER ""Test artist 2""
INDEX 01 04:12:00
-", contentString);
+", content);
}
[TestMethod]
@@ -212,7 +211,6 @@ public void GenerateExportfiles_WithSections_ReturnsExportFiles()
Assert.AreEqual(section3.End, result.Last().End);
var content = result.Last().Content;
Assert.IsNotNull(content);
- var contentString = Encoding.UTF8.GetString(content);
Assert.AreEqual(@"TITLE ""Test title cuesheet""
PERFORMER ""Test artist cuesheet""
FILE ""Test audiofile.mp3"" MP3
@@ -224,7 +222,7 @@ TRACK 02 AUDIO
TITLE ""Test title 6""
PERFORMER ""Test artist 6""
INDEX 01 01:54:00
-", contentString);
+", content);
}
[TestMethod]
diff --git a/AudioCuesheetEditor.Tests/Services/IO/ExportfileGeneratorTests.cs b/AudioCuesheetEditor.Tests/Services/IO/ExportfileGeneratorTests.cs
index e9c1339a..98246d33 100644
--- a/AudioCuesheetEditor.Tests/Services/IO/ExportfileGeneratorTests.cs
+++ b/AudioCuesheetEditor.Tests/Services/IO/ExportfileGeneratorTests.cs
@@ -83,12 +83,11 @@ public void GenerateExportFile_ShouldGenerateExportfile_WithoutSections()
Assert.AreEqual(new TimeSpan(0, 8, 32), result.First().End);
var content = result.First().Content;
Assert.IsNotNull(content);
- var contentString = System.Text.Encoding.UTF8.GetString(content);
Assert.AreEqual(@"Test artist cuesheet - Test title cuesheet
1 Test artist 1 - Test title 1
2 Test artist 2 - Test title 2
-", contentString);
+", content);
}
[TestMethod]
@@ -173,12 +172,11 @@ public void GenerateExportFile_ShouldGenerateExportfiles_WithSections()
Assert.AreEqual(section3.End, result.Last().End);
var content = result.Last().Content;
Assert.IsNotNull(content);
- var contentString = System.Text.Encoding.UTF8.GetString(content);
Assert.AreEqual(@"Test artist cuesheet - Test title cuesheet
1 Test artist 5 - Test title 5
2 Test artist 6 - Test title 6
-", contentString);
+", content);
}
[TestMethod]
diff --git a/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor b/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor
index be6613a6..bf701eee 100644
--- a/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor
+++ b/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor
@@ -48,7 +48,7 @@ along with Foobar. If not, see
@context.End
@{
var ariaLabel = $"Download-{context.Name}";
-
+
}
diff --git a/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor b/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor
index 114bbcbe..8e82bef7 100644
--- a/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor
+++ b/AudioCuesheetEditor/Shared/Export/DisplayExportfileContent.razor
@@ -25,8 +25,8 @@ along with Foobar. If not, see
- @_localizer["Copy"]
-
+ @_localizer["Copy"]
+
From da6f2280a3289b788f317be06c84b852e33c0b00 Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Fri, 6 Mar 2026 14:05:49 +0100
Subject: [PATCH 44/48] Update AppBar.razor
---
AudioCuesheetEditor/Shared/AppBar.razor | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/AudioCuesheetEditor/Shared/AppBar.razor b/AudioCuesheetEditor/Shared/AppBar.razor
index 2a5d3638..d4f89c8c 100644
--- a/AudioCuesheetEditor/Shared/AppBar.razor
+++ b/AudioCuesheetEditor/Shared/AppBar.razor
@@ -46,7 +46,7 @@ along with Foobar. If not, see
@if (DisplayFileMenu)
{
-
+
From 943431c131acc520ebacc27dbc2ad100f642ef0f Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Mon, 9 Mar 2026 16:41:22 +0100
Subject: [PATCH 45/48] fix upload dialog
---
AudioCuesheetEditor/Model/IO/FileUpload.cs | 25 +++++
.../Services/IO/FileInputManager.cs | 93 ++++++++++++-------
.../Services/IO/IFileInputManager.cs | 28 ++++--
.../Services/IO/ImportManager.cs | 53 ++++-------
AudioCuesheetEditor/Shared/AppBar.razor | 4 +-
.../Shared/Cuesheet/CuesheetData.razor | 29 ++++--
.../Shared/Cuesheet/EditSections.razor | 2 +-
.../Shared/Inputs/FileDropOverlay.razor | 8 +-
.../Shared/Inputs/TextField.razor | 5 +-
9 files changed, 157 insertions(+), 90 deletions(-)
create mode 100644 AudioCuesheetEditor/Model/IO/FileUpload.cs
diff --git a/AudioCuesheetEditor/Model/IO/FileUpload.cs b/AudioCuesheetEditor/Model/IO/FileUpload.cs
new file mode 100644
index 00000000..a8df1ee7
--- /dev/null
+++ b/AudioCuesheetEditor/Model/IO/FileUpload.cs
@@ -0,0 +1,25 @@
+//This file is part of AudioCuesheetEditor.
+
+//AudioCuesheetEditor is free software: you can redistribute it and/or modify
+//it under the terms of the GNU General Public License as published by
+//the Free Software Foundation, either version 3 of the License, or
+//(at your option) any later version.
+
+//AudioCuesheetEditor is distributed in the hope that it will be useful,
+//but WITHOUT ANY WARRANTY; without even the implied warranty of
+//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+//GNU General Public License for more details.
+
+//You should have received a copy of the GNU General Public License
+//along with Foobar. If not, see
+//.
+namespace AudioCuesheetEditor.Model.IO
+{
+ public class FileUpload(string name, string contentType, string? content = null, string? objectUrl = null)
+ {
+ public string Name { get; set; } = name;
+ public string ContentType { get; set; } = contentType;
+ public string? Content { get; set; } = content;
+ public string? ObjectUrl { get; set; } = objectUrl;
+ }
+}
diff --git a/AudioCuesheetEditor/Services/IO/FileInputManager.cs b/AudioCuesheetEditor/Services/IO/FileInputManager.cs
index a47eb4f5..6bbabfae 100644
--- a/AudioCuesheetEditor/Services/IO/FileInputManager.cs
+++ b/AudioCuesheetEditor/Services/IO/FileInputManager.cs
@@ -28,12 +28,12 @@ public class FileInputManager(IJSRuntime jsRuntime, HttpClient httpClient, ILogg
private readonly HttpClient _httpClient = httpClient;
private readonly ILogger _logger = logger;
- public AudioCodec? GetAudioCodec(IBrowserFile browserFile)
+ public AudioCodec? GetAudioCodec(string? fileContentType, string fileName)
{
AudioCodec? foundAudioCodec = null;
- var extension = Path.GetExtension(browserFile.Name);
+ var extension = Path.GetExtension(fileName);
// First search with mime type and file extension
- var audioCodecsFound = Audiofile.AudioCodecs.Where(x => x.MimeType.Equals(browserFile.ContentType, StringComparison.OrdinalIgnoreCase) && x.FileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase));
+ var audioCodecsFound = Audiofile.AudioCodecs.Where(x => x.MimeType.Equals(fileContentType, StringComparison.OrdinalIgnoreCase) && x.FileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase));
if (audioCodecsFound.Count() <= 1)
{
foundAudioCodec = audioCodecsFound.FirstOrDefault();
@@ -41,63 +41,62 @@ public class FileInputManager(IJSRuntime jsRuntime, HttpClient httpClient, ILogg
if (foundAudioCodec == null)
{
// Second search with mime type or file extension
- audioCodecsFound = Audiofile.AudioCodecs.Where(x => x.MimeType.Equals(browserFile.ContentType, StringComparison.OrdinalIgnoreCase) || x.FileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase));
+ audioCodecsFound = Audiofile.AudioCodecs.Where(x => x.MimeType.Equals(fileContentType, StringComparison.OrdinalIgnoreCase) || x.FileExtension.Equals(extension, StringComparison.OrdinalIgnoreCase));
foundAudioCodec = audioCodecsFound.FirstOrDefault();
}
return foundAudioCodec;
}
- public bool IsValidAudiofile(IBrowserFile browserFile)
+ public bool IsValidAudiofile(string? fileContentType, string fileName)
{
- var codec = GetAudioCodec(browserFile);
- return codec != null;
+ return GetAudioCodec(fileContentType, fileName) != null;
}
- public Boolean CheckFileMimeType(IBrowserFile file, String mimeType, IEnumerable fileExtensions)
+ public bool CheckFileMimeType(string? fileContentType, string fileName, string mimeType, IEnumerable fileExtensions)
{
+ //TODO: Tests
if (_logger.IsEnabled(LogLevel.Debug))
{
- _logger.LogDebug("CheckFileMimeType called with file: file.Name: '{FileName}', file.ContentType: '{ContentType}', mimeType: '{MimeType}', fileExtensions: '{fileExtensions}'", file.Name, file.ContentType, mimeType, fileExtensions);
+ _logger.LogDebug("CheckFileMimeType called with file: file.Name: '{FileName}', file.ContentType: '{ContentType}', mimeType: '{MimeType}', fileExtensions: '{fileExtensions}'", fileName, fileContentType, mimeType, fileExtensions);
}
Boolean fileMimeTypeMatches = false;
- if ((file != null) && (String.IsNullOrEmpty(mimeType) == false))
+ if (String.IsNullOrEmpty(mimeType) == false)
{
- if (String.IsNullOrEmpty(file.ContentType) == false)
+ if (String.IsNullOrEmpty(fileContentType) == false)
{
if (mimeType.EndsWith("/*"))
{
var mainType = mimeType[..^1];
- fileMimeTypeMatches = file.ContentType.StartsWith(mainType, StringComparison.CurrentCultureIgnoreCase);
+ fileMimeTypeMatches = fileContentType.StartsWith(mainType, StringComparison.CurrentCultureIgnoreCase);
}
else
{
- fileMimeTypeMatches = file.ContentType.Equals(mimeType, StringComparison.CurrentCultureIgnoreCase);
+ fileMimeTypeMatches = fileContentType.Equals(mimeType, StringComparison.CurrentCultureIgnoreCase);
}
}
if ((fileMimeTypeMatches == false) && (fileExtensions.Any()))
{
//Try to find by file extension
- var extension = Path.GetExtension(file.Name);
+ var extension = Path.GetExtension(fileName);
fileMimeTypeMatches = fileExtensions.Any(x => x.Equals(extension, StringComparison.CurrentCultureIgnoreCase));
}
}
return fileMimeTypeMatches;
}
- public async Task CreateAudiofileAsync(String? fileInputId, IBrowserFile? browserFile, Action>? afterContentStreamLoaded = null)
+ public async Task CreateAudiofileAsync(FileUpload fileUpload, Action>? afterContentStreamLoaded = null)
{
Audiofile? audiofile = null;
- if ((String.IsNullOrEmpty(fileInputId) == false) && (browserFile != null))
+ if (fileUpload.ObjectUrl != null)
{
// Check file mime type
- var codec = GetAudioCodec(browserFile);
+ var codec = GetAudioCodec(fileUpload.ContentType, fileUpload.Name);
if (codec != null)
{
- var audioFileObjectURL = await _jsRuntime.InvokeAsync("getObjectURLFromMudFileUpload", fileInputId);
- audiofile = new Audiofile(browserFile.Name, audioFileObjectURL, codec);
- if (String.IsNullOrEmpty(audioFileObjectURL) == false)
+ audiofile = new Audiofile(fileUpload.Name, fileUpload.ObjectUrl, codec);
+ if (String.IsNullOrEmpty(fileUpload.ObjectUrl) == false)
{
- var request = new HttpRequestMessage(HttpMethod.Get, audioFileObjectURL);
+ var request = new HttpRequestMessage(HttpMethod.Get, fileUpload.ObjectUrl);
//TODO: Enable when https://github.com/NeoCoderMatrix86/AudioCuesheetEditor/issues/524 gets done
request.SetBrowserRequestStreamingEnabled(false);
@@ -119,27 +118,25 @@ public Boolean CheckFileMimeType(IBrowserFile file, String mimeType, IEnumerable
return audiofile;
}
- public CDTextfile? CreateCDTextfile(IBrowserFile? browserFile)
+ public CDTextfile? CreateCDTextfile(string? fileContentType, string fileName)
{
- CDTextfile? cdTextfile = null;
- if (browserFile != null)
+ CDTextfile? cdTextfile;
+ if (CheckFileMimeType(fileContentType, fileName, FileMimeTypes.Text, [FileExtensions.CDTextfile]))
{
- if (CheckFileMimeType(browserFile, FileMimeTypes.Text, [FileExtensions.CDTextfile]))
- {
- cdTextfile = new CDTextfile(browserFile.Name);
- }
- else
- {
- throw new ArgumentException("The cdtextfile provided is not of a valid type.");
- }
+ cdTextfile = new CDTextfile(fileName);
+ }
+ else
+ {
+ throw new ArgumentException("The cdtextfile provided is not of a valid type.");
}
return cdTextfile;
}
///
- public bool IsValidForImportView(IBrowserFile browserFile)
+ public bool IsValidForImportView(string? fileContentType, string fileName)
{
- return CheckFileMimeType(browserFile, FileMimeTypes.Text, [FileExtensions.Text, FileExtensions.HTML]);
+ //TODO: Tests
+ return CheckFileMimeType(fileContentType, fileName, FileMimeTypes.Text, [FileExtensions.Text, FileExtensions.HTML]);
}
///
@@ -148,5 +145,33 @@ public async Task ReadFileContentAsync(IBrowserFile browserFile)
var fileContent = new StreamContent(browserFile.OpenReadStream());
return await fileContent.ReadAsStringAsync();
}
+
+ ///
+ public async Task> CreateFileUploadsAsync(IReadOnlyList browserFiles, string? fileInputId = null)
+ {
+ //TODO: Tests
+ List fileUploads = [];
+ foreach (var file in browserFiles)
+ {
+ if (CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, [FileExtensions.Projectfile])
+ || CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet])
+ || IsValidForImportView(file.ContentType, file.Name)
+ || IsValidAudiofile(file.ContentType, file.Name))
+ {
+ string? content = null;
+ string? objectUrl = null;
+ if (IsValidAudiofile(file.ContentType, file.Name))
+ {
+ objectUrl = await _jsRuntime.InvokeAsync("getObjectURLFromMudFileUpload", fileInputId);
+ }
+ else
+ {
+ content = await ReadFileContentAsync(file);
+ }
+ fileUploads.Add(new(file.Name, file.ContentType, content, objectUrl));
+ }
+ }
+ return fileUploads;
+ }
}
}
diff --git a/AudioCuesheetEditor/Services/IO/IFileInputManager.cs b/AudioCuesheetEditor/Services/IO/IFileInputManager.cs
index e5d00d13..026ed5d4 100644
--- a/AudioCuesheetEditor/Services/IO/IFileInputManager.cs
+++ b/AudioCuesheetEditor/Services/IO/IFileInputManager.cs
@@ -15,6 +15,7 @@
//.
using AudioCuesheetEditor.Model.AudioCuesheet;
+using AudioCuesheetEditor.Model.IO;
using AudioCuesheetEditor.Model.IO.Audio;
using Microsoft.AspNetCore.Components.Forms;
@@ -22,22 +23,37 @@ namespace AudioCuesheetEditor.Services.IO
{
public interface IFileInputManager
{
- bool IsValidAudiofile(IBrowserFile browserFile);
- AudioCodec? GetAudioCodec(IBrowserFile browserFile);
- bool CheckFileMimeType(IBrowserFile file, string mimeType, IEnumerable fileExtensions);
- Task CreateAudiofileAsync(string? fileInputId, IBrowserFile? browserFile, Action>? afterContentStreamLoaded = null);
- CDTextfile? CreateCDTextfile(IBrowserFile? browserFile);
+ bool IsValidAudiofile(string? fileContentType, string fileName);
+ AudioCodec? GetAudioCodec(string? fileContentType, string fileName);
+ ///
+ /// Checks if a file content type and name matches given parameters
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ bool CheckFileMimeType(string? fileContentType, string fileName, string mimeType, IEnumerable fileExtensions);
+ Task CreateAudiofileAsync(FileUpload fileUpload, Action>? afterContentStreamLoaded = null);
+ CDTextfile? CreateCDTextfile(string? fileContentType, string fileName);
///
/// Checks if the file can be used for the import view
///
///
///
- bool IsValidForImportView(IBrowserFile browserFile);
+ bool IsValidForImportView(string? fileContentType, string fileName);
///
/// Reads the browser file and gets the file content as string
///
///
///
Task ReadFileContentAsync(IBrowserFile browserFile);
+ ///
+ /// Generates file upload references for files
+ ///
+ ///
+ ///
+ ///
+ Task> CreateFileUploadsAsync(IReadOnlyList browserFiles, string? fileInputId = null);
}
}
\ No newline at end of file
diff --git a/AudioCuesheetEditor/Services/IO/ImportManager.cs b/AudioCuesheetEditor/Services/IO/ImportManager.cs
index 15a32fb3..6817670f 100644
--- a/AudioCuesheetEditor/Services/IO/ImportManager.cs
+++ b/AudioCuesheetEditor/Services/IO/ImportManager.cs
@@ -121,59 +121,47 @@ public void ImportCuesheet()
}
}
- public async Task UploadFilesAsync(IEnumerable files, String? fileInputId = null)
+ public async Task UploadFilesAsync(IEnumerable files)
{
var stopwatch = Stopwatch.StartNew();
var invalidFiles = new List();
foreach (var file in files)
{
- if (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Projectfile, [FileExtensions.Projectfile])
- || _fileInputManager.CheckFileMimeType(file, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet])
- || _fileInputManager.IsValidForImportView(file)
- || _fileInputManager.IsValidAudiofile(file))
+ if (_fileInputManager.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, [FileExtensions.Projectfile])
+ || _fileInputManager.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet])
+ || _fileInputManager.IsValidForImportView(file.ContentType, file.Name)
+ || _fileInputManager.IsValidAudiofile(file.ContentType, file.Name))
{
- if (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Projectfile, [FileExtensions.Projectfile]))
+ if (_fileInputManager.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, [FileExtensions.Projectfile]))
{
- var fileContent = await ReadFileContentAsync(file);
- fileContent.Position = 0;
- using var reader = new StreamReader(fileContent);
- var stringFileContent = reader.ReadToEnd();
_sessionStateContainer.Importfile = new Importfile()
{
- FileContent = stringFileContent,
- FileContentRecognized = stringFileContent,
+ FileContent = file.Content,
+ FileContentRecognized = file.Content,
FileType = ImportFileType.ProjectFile
};
}
- if (_fileInputManager.CheckFileMimeType(file, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet]))
+ if (_fileInputManager.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, [FileExtensions.Cuesheet]))
{
- var fileContent = await ReadFileContentAsync(file);
- fileContent.Position = 0;
- using var reader = new StreamReader(fileContent);
- var stringFileContent = reader.ReadToEnd();
_sessionStateContainer.Importfile = new Importfile()
{
- FileContent = stringFileContent,
- FileContentRecognized = stringFileContent,
+ FileContent = file.Content,
+ FileContentRecognized = file.Content,
FileType = ImportFileType.Cuesheet
};
}
- if (_fileInputManager.IsValidForImportView(file))
+ if (_fileInputManager.IsValidForImportView(file.ContentType, file.Name))
{
- var fileContent = await ReadFileContentAsync(file);
- fileContent.Position = 0;
- using var reader = new StreamReader(fileContent);
- var stringFileContent = reader.ReadToEnd();
_sessionStateContainer.Importfile = new Importfile()
{
- FileContent = stringFileContent,
- FileContentRecognized = stringFileContent,
+ FileContent = file.Content,
+ FileContentRecognized = file.Content,
FileType = ImportFileType.Textfile
};
}
- if (_fileInputManager.IsValidAudiofile(file))
+ if (_fileInputManager.IsValidAudiofile(file.ContentType, file.Name))
{
- var audioFile = await _fileInputManager.CreateAudiofileAsync(fileInputId, file);
+ var audioFile = await _fileInputManager.CreateAudiofileAsync(file);
_sessionStateContainer.ImportAudiofile = audioFile;
}
}
@@ -190,15 +178,6 @@ public async Task UploadFilesAsync(IEnumerable files, String? file
}
}
- private static async Task ReadFileContentAsync(IBrowserFile file)
- {
- var fileContent = new MemoryStream();
- var stream = file.OpenReadStream();
- await stream.CopyToAsync(fileContent);
- stream.Close();
- return fileContent;
- }
-
private static void CopyCuesheet(Cuesheet target, ICuesheet cuesheetToCopy)
{
target.IsImporting = true;
diff --git a/AudioCuesheetEditor/Shared/AppBar.razor b/AudioCuesheetEditor/Shared/AppBar.razor
index d4f89c8c..395d5e3d 100644
--- a/AudioCuesheetEditor/Shared/AppBar.razor
+++ b/AudioCuesheetEditor/Shared/AppBar.razor
@@ -25,6 +25,7 @@ along with Foobar. If not, see
@inject IJSRuntime _jsRuntime
@inject HotKeys _hotKeys
@inject ImportManager _importManager
+@inject IFileInputManager _fileInputManager
@@ -229,7 +230,8 @@ along with Foobar. If not, see
async Task FileUploaded(IBrowserFile file)
{
- await _importManager.UploadFilesAsync([file]);
+ var fileUploads = await _fileInputManager.CreateFileUploadsAsync([file]);
+ await _importManager.UploadFilesAsync(fileUploads);
}
async Task ShowHotkeysDialog()
diff --git a/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor b/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor
index 4d44b0a3..61605a5a 100644
--- a/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor
+++ b/AudioCuesheetEditor/Shared/Cuesheet/CuesheetData.razor
@@ -111,14 +111,22 @@ along with Foobar. If not, see
fileInputAudiofileErrorText = null;
try
{
- Cuesheet.Audiofile = await _fileInputManager.CreateAudiofileAsync(fileInputAudiofileId, browserFile, x =>
+ if (browserFile != null)
{
- if (Cuesheet.RecalculateLastTrackEnd())
+ var fileUpload = await _fileInputManager.CreateFileUploadsAsync([browserFile], fileInputAudiofileId);
+ Cuesheet.Audiofile = await _fileInputManager.CreateAudiofileAsync(fileUpload.Single(), x =>
{
- TraceChangeManager.MergeLastEditWithEdit(x => x.Changes.All(y => y.TraceableObject == Cuesheet && y.TraceableChange.PropertyName == nameof(Audiofile)));
- }
- StateHasChanged();
- });
+ if (Cuesheet.RecalculateLastTrackEnd())
+ {
+ TraceChangeManager.MergeLastEditWithEdit(x => x.Changes.All(y => y.TraceableObject == Cuesheet && y.TraceableChange.PropertyName == nameof(Audiofile)));
+ }
+ StateHasChanged();
+ });
+ }
+ else
+ {
+ Cuesheet.Audiofile = null;
+ }
}
catch(ArgumentException ae)
{
@@ -157,7 +165,14 @@ along with Foobar. If not, see
fileInputCDTextfileErrorText = null;
try
{
- Cuesheet.CDTextfile = _fileInputManager.CreateCDTextfile(browserFile);
+ if (browserFile != null)
+ {
+ Cuesheet.CDTextfile = _fileInputManager.CreateCDTextfile(browserFile.ContentType, browserFile.Name);
+ }
+ else
+ {
+ Cuesheet.CDTextfile = null;
+ }
}
catch (ArgumentException ae)
{
diff --git a/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor b/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor
index d1abd5cb..2c998de5 100644
--- a/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor
+++ b/AudioCuesheetEditor/Shared/Cuesheet/EditSections.razor
@@ -142,7 +142,7 @@ along with Foobar. If not, see
void AudiofileSelected(CuesheetSection section, IBrowserFile? browserFile)
{
- if ((browserFile != null) && (_fileInputManager.IsValidAudiofile(browserFile) == true))
+ if ((browserFile != null) && (_fileInputManager.IsValidAudiofile(browserFile.ContentType, browserFile.Name) == true))
{
section.AudiofileName = browserFile?.Name;
}
diff --git a/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor b/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor
index 2f22ee2f..deb044b1 100644
--- a/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor
+++ b/AudioCuesheetEditor/Shared/Inputs/FileDropOverlay.razor
@@ -23,6 +23,7 @@ along with Foobar. If not, see
@inject IStringLocalizer _localizer
@inject ImportManager _importManager
@inject DialogManager _dialogManager
+@inject IFileInputManager _fileInputManager
@@ -42,7 +43,7 @@ along with Foobar. If not, see
private DotNetObjectReference? _dotNetRef;
private bool _showOverlay;
private bool _disposed;
-
+
private readonly string _fileUploadId = $"FileDropOverlay_{Guid.NewGuid()}";
protected override async Task OnInitializedAsync()
@@ -78,8 +79,11 @@ along with Foobar. If not, see
try
{
await _dialogManager.ShowLoadingDialogAsync();
+ var fileUploads = await _fileInputManager.CreateFileUploadsAsync(files, _fileUploadId);
_showOverlay = false;
- await _importManager.UploadFilesAsync(files, _fileUploadId);
+ await InvokeAsync(StateHasChanged);
+ await Task.Yield();
+ await _importManager.UploadFilesAsync(fileUploads);
await _jsRuntime.InvokeVoidAsync("globalFileDrag.reset", _dotNetRef);
}
finally
diff --git a/AudioCuesheetEditor/Shared/Inputs/TextField.razor b/AudioCuesheetEditor/Shared/Inputs/TextField.razor
index dfc5d589..fea7e524 100644
--- a/AudioCuesheetEditor/Shared/Inputs/TextField.razor
+++ b/AudioCuesheetEditor/Shared/Inputs/TextField.razor
@@ -44,9 +44,10 @@ along with Foobar. If not, see
async Task FileUploaded(InputFileChangeEventArgs e)
{
- if (_fileInputManager.IsValidForImportView(e.File))
+ var file = e.File;
+ if (_fileInputManager.IsValidForImportView(file.ContentType, file.Name))
{
- var fileContent = await _fileInputManager.ReadFileContentAsync(e.File);
+ var fileContent = await _fileInputManager.ReadFileContentAsync(file);
await TextChanged.InvokeAsync(fileContent);
}
}
From 6bcef35902dda9d578d6060adb80c6cc3ff9947c Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Mon, 9 Mar 2026 16:51:21 +0100
Subject: [PATCH 46/48] Update FileInputManagerTests.cs
---
.../Services/IO/FileInputManagerTests.cs | 69 ++++++++++---------
1 file changed, 36 insertions(+), 33 deletions(-)
diff --git a/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs b/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs
index f78b881c..e6d4bdce 100644
--- a/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs
+++ b/AudioCuesheetEditor.Tests/Services/IO/FileInputManagerTests.cs
@@ -15,7 +15,6 @@
//.
using AudioCuesheetEditor.Model.IO.Audio;
using AudioCuesheetEditor.Services.IO;
-using Microsoft.AspNetCore.Components.Forms;
using Microsoft.Extensions.Logging;
using Microsoft.JSInterop;
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -34,11 +33,12 @@ public void CheckFileMimeType_ReturnsTrue_WhenContentTypeDoesNotMatchButExtensio
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("test.mp3", "audio/wav");
+ var fileName = "test.mp3";
+ var contentType = "audio/wav";
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.CheckFileMimeType(file, "audio/mpeg", [".mp3"]);
+ var result = manager.CheckFileMimeType(contentType, fileName, "audio/mpeg", [".mp3"]);
// Assert
Assert.IsTrue(result);
@@ -51,11 +51,12 @@ public void CheckFileMimeType_ReturnsTrue_WhenContentTypeDoesMatchButNotExtensio
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("test.mpeg", "audio/mpeg");
+ var fileName = "test.mpeg";
+ var contentType = "audio/mpeg";
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.CheckFileMimeType(file, "audio/mpeg", [".mp3", ".txt"]);
+ var result = manager.CheckFileMimeType(contentType, fileName, "audio/mpeg", [".mp3", ".txt"]);
// Assert
Assert.IsTrue(result);
@@ -68,11 +69,12 @@ public void CheckFileMimeType_ReturnsFalse_WhenExtensionDoesNotMatchAndContentTy
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("test.flac", string.Empty);
+ var fileName = "test.flac";
+ var contentType = string.Empty;
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.CheckFileMimeType(file, "audio/flac", [".mp3"]);
+ var result = manager.CheckFileMimeType(contentType, fileName, "audio/flac", [".mp3"]);
// Assert
Assert.IsFalse(result);
@@ -85,11 +87,12 @@ public void CheckFileMimeType_ReturnsTrue_WhenContentTypeAndExtensionMatch()
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("test.wav", "audio/wave");
+ var fileName = "test.wav";
+ var contentType = "audio/wave";
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.CheckFileMimeType(file, "audio/wave", [".wav"]);
+ var result = manager.CheckFileMimeType(contentType, fileName, "audio/wave", [".wav"]);
// Assert
Assert.IsTrue(result);
@@ -102,11 +105,12 @@ public void CheckFileMimeType_ReturnsTrue_WhenContentMainTypeMatch()
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("history.txt", "text/plain");
+ var fileName = "history.txt";
+ var contentType = "text/plain";
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.CheckFileMimeType(file, "text/*", [".txt", ".text"]);
+ var result = manager.CheckFileMimeType(contentType, fileName, "text/*", [".txt", ".text"]);
// Assert
Assert.IsTrue(result);
@@ -119,11 +123,12 @@ public void IsValidAudiofile_ReturnsTrue_WithValidAudiocodec()
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("test.wav", "audio/wav");
+ var fileName = "test.wav";
+ var contentType = "audio/wav";
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.IsValidAudiofile(file);
+ var result = manager.IsValidAudiofile(contentType, fileName);
// Assert
Assert.IsTrue(result);
@@ -136,11 +141,12 @@ public void IsValidAudiofile_ReturnsFalse_WithInvalidAudiocodecAndExtension()
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("test.mock", "just a fantasy");
+ var fileName = "test.mock";
+ var contentType = "just a fantasy";
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.IsValidAudiofile(file);
+ var result = manager.IsValidAudiofile(contentType, fileName);
// Assert
Assert.IsFalse(result);
@@ -153,11 +159,12 @@ public void GetAudioCodec_ReturnsAudiocodec_WhenContentTypeMatches()
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("test.wbem", "audio/webm");
+ var fileName = "test.wbem";
+ var contentType = "audio/webm";
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.GetAudioCodec(file);
+ var result = manager.GetAudioCodec(contentType, fileName);
// Assert
Assert.IsNotNull(result);
@@ -171,11 +178,12 @@ public void GetAudioCodec_ReturnsAudiocodec_WhenContentTypeAndFileExtensionMatch
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("test.webm", "audio/webm");
+ var fileName = "test.wbem";
+ var contentType = "audio/webm";
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.GetAudioCodec(file);
+ var result = manager.GetAudioCodec(contentType, fileName);
// Assert
Assert.IsNotNull(result);
@@ -189,11 +197,12 @@ public void GetAudioCodec_ReturnsNull_WhenContentTypeAndFileExtensionNotMatch()
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("test.acx", "fantasy stuff");
+ var fileName = "test.acx";
+ var contentType = "fantasy stuff";
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.GetAudioCodec(file);
+ var result = manager.GetAudioCodec(contentType, fileName);
// Assert
Assert.IsNull(result);
@@ -206,11 +215,12 @@ public void IsValidForImportView_ReturnsTrue_WhenFileIsHtml()
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("test.html", "text/html");
+ var fileName = "test.html";
+ var contentType = "text/html";
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.IsValidForImportView(file);
+ var result = manager.IsValidForImportView(contentType, fileName);
// Assert
Assert.IsTrue(result);
@@ -223,22 +233,15 @@ public void IsValidForImportView_ReturnsFalse_WhenFileIsBinary()
var jsRuntimeMock = new Mock();
var httpClientMock = new Mock();
var loggerMock = new Mock>();
- var file = CreateBrowserFile("test.dat", "application/octet-stream");
+ var fileName = "test.dat";
+ var contentType = "application/octet-stream";
var manager = new FileInputManager(jsRuntimeMock.Object, httpClientMock.Object, loggerMock.Object);
// Act
- var result = manager.IsValidForImportView(file);
+ var result = manager.IsValidForImportView(contentType, fileName);
// Assert
Assert.IsFalse(result);
}
-
- private static IBrowserFile CreateBrowserFile(string name, string contentType)
- {
- var fileMock = new Mock();
- fileMock.Setup(f => f.Name).Returns(name);
- fileMock.Setup(f => f.ContentType).Returns(contentType);
- return fileMock.Object;
- }
}
}
\ No newline at end of file
From 47a52b590ed43c1c0a67fa79b239dd95f75d203d Mon Sep 17 00:00:00 2001
From: Sven <40752681+NeoCoderMatrix86@users.noreply.github.com>
Date: Mon, 9 Mar 2026 16:58:25 +0100
Subject: [PATCH 47/48] Update ImportManagerTests.cs
---
.../Services/IO/ImportManagerTests.cs | 51 ++++++++-----------
1 file changed, 21 insertions(+), 30 deletions(-)
diff --git a/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs b/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs
index a1481c15..5b6b2ec9 100644
--- a/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs
+++ b/AudioCuesheetEditor.Tests/Services/IO/ImportManagerTests.cs
@@ -215,10 +215,10 @@ public async Task UploadFilesAsync_ProjectFile_ImportsCorrectly()
{
// Arrange
var fileContent = "This is the content";
- var file = CreateBrowserFileMock("test.projectfile", fileContent);
- _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Projectfile, It.IsAny>())).Returns(true);
- _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(false);
- _fileInputManagerMock.Setup(f => f.IsValidForImportView(file)).Returns(false);
+ var file = new FileUpload("test.projectfile", "text/plain", fileContent);
+ _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, It.IsAny>())).Returns(true);
+ _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(false);
+ _fileInputManagerMock.Setup(f => f.IsValidForImportView(file.ContentType, file.Name)).Returns(false);
IImportfile? sessionStateContainerImportfile = null;
_sessionStateContainerMock.SetupSet(x => x.Importfile = It.IsAny()).Callback(x => sessionStateContainerImportfile = x);
@@ -237,11 +237,11 @@ public async Task UploadFilesAsync_CuesheetFile_ImportsCorrectly()
{
// Arrange
var fileContent = "Cuesheet file content";
- var file = CreateBrowserFileMock("test.cue", fileContent);
-
- _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Projectfile, It.IsAny>())).Returns(false);
- _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(true);
- _fileInputManagerMock.Setup(f => f.IsValidForImportView(file)).Returns(false);
+ var file = new FileUpload("test.cue", "text/plain", fileContent);
+
+ _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, It.IsAny>())).Returns(false);
+ _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(true);
+ _fileInputManagerMock.Setup(f => f.IsValidForImportView(file.ContentType, file.Name)).Returns(false);
IImportfile? sessionStateContainerImportfile = null;
_sessionStateContainerMock.SetupSet(x => x.Importfile = It.IsAny()).Callback(x => sessionStateContainerImportfile = x);
@@ -260,11 +260,11 @@ public async Task UploadFilesAsync_TextFile_ImportsCorrectly()
{
// Arrange
var fileContent = "TextFileContent";
- var file = CreateBrowserFileMock("test.txt", fileContent);
-
- _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Projectfile, It.IsAny>())).Returns(false);
- _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(false);
- _fileInputManagerMock.Setup(f => f.IsValidForImportView(file)).Returns(true);
+ var file = new FileUpload("test.txt", "text/plain", fileContent);
+
+ _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, It.IsAny>())).Returns(false);
+ _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(false);
+ _fileInputManagerMock.Setup(f => f.IsValidForImportView(file.ContentType, file.Name)).Returns(true);
IImportfile? sessionStateContainerImportfile = null;
_sessionStateContainerMock.SetupSet(x => x.Importfile = It.IsAny()).Callback(x => sessionStateContainerImportfile = x);
@@ -282,13 +282,13 @@ public async Task UploadFilesAsync_TextFile_ImportsCorrectly()
public async Task UploadFilesAsync_WithAudiofile_ImportsCorrectly()
{
// Arrange
- var file = CreateBrowserFileMock("test.mp3");
-
- _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Projectfile, It.IsAny>())).Returns(false);
- _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file, FileMimeTypes.Cuesheet, It.IsAny>())).Returns(false);
- _fileInputManagerMock.Setup(f => f.IsValidForImportView(file)).Returns(false);
- _fileInputManagerMock.Setup(f => f.IsValidAudiofile(file)).Returns(true);
- _fileInputManagerMock.Setup(f => f.CreateAudiofileAsync(It.IsAny(), It.IsAny(), It.IsAny>?>())).ReturnsAsync(new AudioCuesheetEditor.Model.IO.Audio.Audiofile(file.Name));
+ var file = new FileUpload("test.mp3", "audio/mpeg");
+
+ _fileInputManagerMock.Setup(f => f.CheckFileMimeType(file.ContentType, file.Name, FileMimeTypes.Projectfile, It.IsAny