diff --git a/.editorconfig b/.editorconfig index 9d73dbfd..d735b55b 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,7 +1 @@ -[*.cs] - -# IDE0016: throw-Ausdruck verwenden -csharp_style_throw_expression = false:suggestion - -# IDE0057: Bereichsoperator verwenden -csharp_style_prefer_range_operator = false:suggestion +[*.cs] \ No newline at end of file diff --git a/.github/workflows/build_pipeline.yml b/.github/workflows/build_pipeline.yml index f6948c68..9cc55ee8 100644 --- a/.github/workflows/build_pipeline.yml +++ b/.github/workflows/build_pipeline.yml @@ -15,22 +15,22 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v6 - # version can be found here https://dotnet.microsoft.com/en-us/download/dotnet/9.0 + # version can be found here https://dotnet.microsoft.com/en-us/download/dotnet/10.0 - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: - dotnet-version: 9.0.x + dotnet-version: 10.0.x - name: Install wasm-tools - run: dotnet workload install wasm-tools-net9 + run: dotnet workload install wasm-tools - name: Build release run: dotnet publish AudioCuesheetEditor --configuration Release --output release - name: Upload Build Artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: AudioCuesheetEditor-Release path: ./release @@ -45,7 +45,7 @@ jobs: }} steps: - name: Download Build Artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: name: AudioCuesheetEditor-Release - name: Deploy to Netlify diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 0bab000c..03c1fa8f 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -9,25 +9,25 @@ jobs: run-tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4.2.2 + - uses: actions/checkout@v6 - name: Set timestamp variable id: timestamp run: echo "datetime=$(date +'%Y-%m-%d_%H-%M-%S')" >> $GITHUB_OUTPUT - # version can be found here https://dotnet.microsoft.com/en-us/download/dotnet/9.0 + # version can be found here https://dotnet.microsoft.com/en-us/download/dotnet/10.0 - name: Setup .NET - uses: actions/setup-dotnet@v4 + uses: actions/setup-dotnet@v5 with: - dotnet-version: 9.0.x + dotnet-version: 10.0.x - name: Install wasm-tools - run: dotnet workload install wasm-tools-net9 + run: dotnet workload install wasm-tools - name: Build & Install run: dotnet build - name: Ensure browsers are installed - run: pwsh AudioCuesheetEditor.End2EndTests/bin/Debug/net9.0/playwright.ps1 install --with-deps + run: pwsh AudioCuesheetEditor.End2EndTests/bin/Debug/net10.0/playwright.ps1 install --with-deps - name: Start App run: dotnet run --project AudioCuesheetEditor & @@ -59,8 +59,8 @@ jobs: - name: Upload traces if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: playwright-traces-${{ steps.timestamp.outputs.datetime }} - path: AudioCuesheetEditor.End2EndTests/bin/Debug/net9.0/playwright-traces/ + path: AudioCuesheetEditor.End2EndTests/bin/Debug/net10.0/playwright-traces/ if-no-files-found: ignore diff --git a/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj b/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj index 4c5c4ccd..85d73e01 100644 --- a/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj +++ b/AudioCuesheetEditor.End2EndTests/AudioCuesheetEditor.End2EndTests.csproj @@ -1,7 +1,7 @@ - net9.0 + net10.0 latest enable enable 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.End2EndTests/Models/AppBar.cs b/AudioCuesheetEditor.End2EndTests/Models/AppBar.cs index 6129ffd9..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(); } @@ -48,6 +42,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() @@ -81,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.Tests/AudioCuesheetEditor.Tests.csproj b/AudioCuesheetEditor.Tests/AudioCuesheetEditor.Tests.csproj index 0e10bc14..dc6ac4ec 100644 --- a/AudioCuesheetEditor.Tests/AudioCuesheetEditor.Tests.csproj +++ b/AudioCuesheetEditor.Tests/AudioCuesheetEditor.Tests.csproj @@ -1,9 +1,9 @@ - net9.0 + net10.0 latest - enable + enable false true Exe diff --git a/AudioCuesheetEditor/AudioCuesheetEditor.csproj b/AudioCuesheetEditor/AudioCuesheetEditor.csproj index ec6a9afa..d7bb3822 100644 --- a/AudioCuesheetEditor/AudioCuesheetEditor.csproj +++ b/AudioCuesheetEditor/AudioCuesheetEditor.csproj @@ -1,13 +1,13 @@ - net9.0 + net10.0 true enable enable https://github.com/NeoCoderMatrix86/AudioCuesheetEditor 3.0 - 10.4.0 + 11.0.0 false true true @@ -22,13 +22,13 @@ - - + + - - - + + + 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); diff --git a/AudioCuesheetEditor/Program.cs b/AudioCuesheetEditor/Program.cs index dc5ef590..ce09002c 100644 --- a/AudioCuesheetEditor/Program.cs +++ b/AudioCuesheetEditor/Program.cs @@ -27,8 +27,6 @@ using Microsoft.AspNetCore.Components.Web; using Microsoft.AspNetCore.Components.WebAssembly.Hosting; using MudBlazor.Services; -using System.Globalization; -using System.Reflection; using Toolbelt.Blazor.Extensions.DependencyInjection; var builder = WebAssemblyHostBuilder.CreateDefault(args); @@ -73,29 +71,8 @@ builder.Services.AddHotKeys2(); -// TODO: Remove this when https://github.com/dotnet/aspnetcore/issues/56824 is fixed - -// Get current localization culture -var currentCulture = CultureInfo.DefaultThreadCurrentCulture; - -// Get WASM culture provider via reflection -var type = Assembly.GetAssembly(typeof(WebAssemblyHost))!.GetType("Microsoft.AspNetCore.Components.WebAssembly.Hosting.WebAssemblyCultureProvider"); -var instance = type - !.GetProperty("Instance", BindingFlags.Public | BindingFlags.Static) - ?.GetValue(null); - -// Swap out the "current culture" for the UI (localization) culture -CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("de-DE"); -// Load the satellite assemblies -await (ValueTask)instance! - .GetType() - .GetMethod("LoadCurrentCultureResourcesAsync", BindingFlags.Public | BindingFlags.Instance)! - .Invoke(instance, [])!; -// Swap the culture back -CultureInfo.DefaultThreadCurrentCulture = currentCulture; - var host = builder.Build(); await host.SetCultureFromConfigurationAsync(); -await host.RunAsync(); +await host.RunAsync(); \ No newline at end of file diff --git a/AudioCuesheetEditor/Services/IO/FileInputManager.cs b/AudioCuesheetEditor/Services/IO/FileInputManager.cs index 78154ac4..a47eb4f5 100644 --- a/AudioCuesheetEditor/Services/IO/FileInputManager.cs +++ b/AudioCuesheetEditor/Services/IO/FileInputManager.cs @@ -17,6 +17,7 @@ using AudioCuesheetEditor.Model.IO; using AudioCuesheetEditor.Model.IO.Audio; using Microsoft.AspNetCore.Components.Forms; +using Microsoft.AspNetCore.Components.WebAssembly.Http; using Microsoft.JSInterop; namespace AudioCuesheetEditor.Services.IO @@ -54,7 +55,10 @@ public bool IsValidAudiofile(IBrowserFile browserFile) public Boolean CheckFileMimeType(IBrowserFile file, String mimeType, IEnumerable fileExtensions) { - _logger.LogDebug("CheckFileMimeType called with file: file.Name: '{FileName}', file.ContentType: '{ContentType}', mimeType: '{MimeType}', fileExtensions: '{fileExtensions}'", file.Name, file.ContentType, mimeType, fileExtensions); + 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); + } Boolean fileMimeTypeMatches = false; if ((file != null) && (String.IsNullOrEmpty(mimeType) == false)) { @@ -62,7 +66,7 @@ public Boolean CheckFileMimeType(IBrowserFile file, String mimeType, IEnumerable { if (mimeType.EndsWith("/*")) { - var mainType = mimeType.Substring(0, mimeType.Length - 1); + var mainType = mimeType[..^1]; fileMimeTypeMatches = file.ContentType.StartsWith(mainType, StringComparison.CurrentCultureIgnoreCase); } else @@ -93,7 +97,12 @@ public Boolean CheckFileMimeType(IBrowserFile file, String mimeType, IEnumerable audiofile = new Audiofile(browserFile.Name, audioFileObjectURL, codec); if (String.IsNullOrEmpty(audioFileObjectURL) == false) { - var loadContentStreamTask = _httpClient.GetStreamAsync(audioFileObjectURL) + var request = new HttpRequestMessage(HttpMethod.Get, audioFileObjectURL); + //TODO: Enable when https://github.com/NeoCoderMatrix86/AudioCuesheetEditor/issues/524 gets done + request.SetBrowserRequestStreamingEnabled(false); + + var response = await _httpClient.SendAsync(request); + var loadContentStreamTask = response.Content.ReadAsStreamAsync() .ContinueWith(x => audiofile.ContentStream = x.Result); if (afterContentStreamLoaded != null) { diff --git a/AudioCuesheetEditor/Services/IO/ImportManager.cs b/AudioCuesheetEditor/Services/IO/ImportManager.cs index 0d1a8745..15a32fb3 100644 --- a/AudioCuesheetEditor/Services/IO/ImportManager.cs +++ b/AudioCuesheetEditor/Services/IO/ImportManager.cs @@ -52,7 +52,10 @@ public void ImportData(String? data) FileType = ImportFileType.Textfile }; stopwatch.Stop(); - _logger.LogDebug("ImportData duration: {stopwatch.Elapsed}", stopwatch.Elapsed); + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug("ImportData duration: {stopwatch.Elapsed}", stopwatch.Elapsed); + } } public async Task AnalyseImportfile() @@ -94,7 +97,10 @@ public async Task AnalyseImportfile() } } stopwatch.Stop(); - _logger.LogDebug("ImportTextAsync duration: {stopwatch.Elapsed}", stopwatch.Elapsed); + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug("ImportTextAsync duration: {stopwatch.Elapsed}", stopwatch.Elapsed); + } } public void ImportCuesheet() @@ -109,7 +115,10 @@ public void ImportCuesheet() } _sessionStateContainer.ResetImport(); stopwatch.Stop(); - _logger.LogDebug("ImportCuesheet duration: {stopwatch.Elapsed}", stopwatch.Elapsed); + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug("ImportCuesheet duration: {stopwatch.Elapsed}", stopwatch.Elapsed); + } } public async Task UploadFilesAsync(IEnumerable files, String? fileInputId = null) @@ -175,7 +184,10 @@ public async Task UploadFilesAsync(IEnumerable files, String? file } UploadFilesFinished?.Invoke(this, invalidFiles); stopwatch.Stop(); - _logger.LogDebug("UploadFilesAsync duration: {stopwatch.Elapsed}", stopwatch.Elapsed); + if (_logger.IsEnabled(LogLevel.Debug)) + { + _logger.LogDebug("UploadFilesAsync duration: {stopwatch.Elapsed}", stopwatch.Elapsed); + } } private static async Task ReadFileContentAsync(IBrowserFile file) diff --git a/AudioCuesheetEditor/Services/UI/LocalizationService.cs b/AudioCuesheetEditor/Services/UI/LocalizationService.cs index c48c8588..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,35 +42,34 @@ public static IReadOnlyCollection AvailableCultures } } - public event EventHandler? LocalizationChanged; - public static CultureInfo SelectedCulture => CultureInfo.DefaultThreadCurrentUICulture ?? CultureInfo.CurrentUICulture; 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); - LocalizationChanged?.Invoke(this, new EventArgs()); + await _localStorageOptionsProvider.SaveOptionsValueAsync(x => x.CultureName!, culture.Name); + await _jsRuntime.InvokeVoidAsync("removeBeforeunload"); + _navigationManager.NavigateTo(_navigationManager.Uri, true); } } - 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; + CultureInfo.DefaultThreadCurrentCulture = newCulture; } - return newCulture != null; + return contains; } } } diff --git a/AudioCuesheetEditor/Shared/AppBar.razor b/AudioCuesheetEditor/Shared/AppBar.razor index 9631153d..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 @@ -59,7 +59,7 @@ along with Foobar. If not, see @foreach (var culture in LocalizationService.AvailableCultures) { - @IsoCountryCodeToFlagEmoji(culture) @culture.DisplayName + @IsoCountryCodeToFlagEmoji(culture) @culture.DisplayName } @@ -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,9 +118,12 @@ along with Foobar. If not, see [Parameter] public Boolean DisplayReset { get; set; } - async Task SelectedCultureChanged(string name) + [Parameter] + public Boolean DisplayHotkeys { get; set; } + + async Task SelectedCultureChanged(CultureInfo culture) { - await base.LocalizationService.ChangeLanguageAsync(name); + await base.LocalizationService.ChangeLanguageAsync(culture); } String GetStyle(CultureInfo cultureInfo) @@ -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/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/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/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/Dialogs/EditTrackModal.razor b/AudioCuesheetEditor/Shared/Dialogs/EditTrackModal.razor index 14886c52..721e7fe9 100644 --- a/AudioCuesheetEditor/Shared/Dialogs/EditTrackModal.razor +++ b/AudioCuesheetEditor/Shared/Dialogs/EditTrackModal.razor @@ -38,7 +38,7 @@ along with Foobar. If not, see Name = EditedTrack.Artist }; } - @@ -57,7 +57,7 @@ along with Foobar. If not, see Title = EditedTrack.Title }; } - 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 - @_localizer["Search"] + @_localizer["Search"] - + @if (DisplayMenu) { - - @_localizer["Rename file"] + + @_localizer["Rename file"] } 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 diff --git a/AudioCuesheetEditor/Shared/Record/AddTrack.razor b/AudioCuesheetEditor/Shared/Record/AddTrack.razor index ba00b751..102317a2 100644 --- a/AudioCuesheetEditor/Shared/Record/AddTrack.razor +++ b/AudioCuesheetEditor/Shared/Record/AddTrack.razor @@ -31,7 +31,7 @@ along with Foobar. If not, see Name = currentRecordingTrack.Artist }; } - @@ -53,7 +53,7 @@ along with Foobar. If not, see Title = currentRecordingTrack.Title }; } - diff --git a/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor b/AudioCuesheetEditor/Shared/TrackList/ArtistColumn.razor index b82322b0..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 ccb84295..68b190ff 100644 --- a/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor +++ b/AudioCuesheetEditor/Shared/TrackList/TitleColumn.razor @@ -20,9 +20,9 @@ along with Foobar. If not, see @inject AutocompleteManager _autocompleteManager @inject ValidationService _validationService - + @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; } } diff --git a/AudioCuesheetEditor/wwwroot/index.html b/AudioCuesheetEditor/wwwroot/index.html index 0f4b486f..9edfd235 100644 --- a/AudioCuesheetEditor/wwwroot/index.html +++ b/AudioCuesheetEditor/wwwroot/index.html @@ -1,9 +1,9 @@ - + - + AudioCuesheetEditor @@ -32,14 +32,13 @@ position: relative; } -
- +

Loading application, please stand by ...

@@ -55,7 +54,7 @@ 🗙
- + 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 [![Build & Deploy](https://github.com/NeoCoderMatrix86/AudioCuesheetEditor/actions/workflows/build_pipeline.yml/badge.svg?branch=master)](https://github.com/NeoCoderMatrix86/AudioCuesheetEditor/actions/workflows/build_pipeline.yml) The current stable version can be found here: https://audiocuesheeteditor.netlify.app/ -### Preview +## Preview [![Build & Deploy](https://github.com/NeoCoderMatrix86/AudioCuesheetEditor/actions/workflows/build_pipeline.yml/badge.svg?branch=development)](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