diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs index bbb02b5a..ea6aefe5 100644 --- a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs +++ b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/ExportTest.cs @@ -35,7 +35,7 @@ public async Task DownloadCuesheet_GeneratesCuesheetFile_WhenCuesheetIsValid() await detailView.EditTrackAsync("Track Artist 1", "Track Title 1"); await bar.OpenExportDialogAsync("Cuesheet"); var downloadTask = TestPage.WaitForDownloadAsync(); - await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download-Cuesheet.cue" }).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/ExportTest.cs b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTest.cs index eb85f017..859ae80d 100644 --- a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTest.cs +++ b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/ExportTest.cs @@ -37,7 +37,7 @@ public async Task DownloadCuesheet_GeneratesCuesheetFile_WhenCuesheetIsValid() await detailView.EditTrackAsync("Track Artist 1", "Track Title 1"); await bar.OpenExportDialogAsync("Cuesheet"); var downloadTask = TestPage.WaitForDownloadAsync(); - await TestPage.GetByRole(AriaRole.Button, new() { Name = "Download-Cuesheet.cue" }).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/TextImportServiceTests.cs b/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs index 8cc1b33c..df44aa1b 100644 --- a/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs +++ b/AudioCuesheetEditor.Tests/Services/IO/TextImportServiceTests.cs @@ -13,13 +13,13 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.AudioCuesheet.Import; using AudioCuesheetEditor.Model.IO.Import; using AudioCuesheetEditor.Model.Options; using AudioCuesheetEditor.Model.Utility; using AudioCuesheetEditor.Services.IO; +using AudioCuesheetEditor.Services.Options; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using System; diff --git a/AudioCuesheetEditor.Tests/Services/Options/LocalStorageOptionsProviderTests.cs b/AudioCuesheetEditor.Tests/Services/Options/LocalStorageOptionsProviderTests.cs new file mode 100644 index 00000000..d23853d7 --- /dev/null +++ b/AudioCuesheetEditor.Tests/Services/Options/LocalStorageOptionsProviderTests.cs @@ -0,0 +1,104 @@ +//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.Model.AudioCuesheet; +using AudioCuesheetEditor.Model.Entity; +using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Services.IO; +using AudioCuesheetEditor.Services.Options; +using AudioCuesheetEditor.Services.UI; +using Microsoft.Extensions.Localization; +using Microsoft.JSInterop; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; + +namespace AudioCuesheetEditor.Tests.Services.Options +{ + [TestClass()] + public class LocalStorageOptionsProviderTests + { + private readonly LocalStorageOptionsProvider _service; + private readonly Mock _jsRuntime = new(); + private readonly Mock> _localizer = new(); + + public LocalStorageOptionsProviderTests() + { + _service = new(_jsRuntime.Object, _localizer.Object); + } + + [TestMethod] + public async Task SaveOptionsAsync_NonValidatableOptions_ReturnsSuccessAsync() + { + // Arrange + var options = new ExportOptions(); + Boolean eventRaised = false; + _service.OptionSaved += delegate + { + eventRaised = true; + }; + // Act + var result = await _service.SaveOptionsAsync(options); + // Assert + Assert.IsTrue(result.IsSuccess); + Assert.IsTrue(eventRaised); + } + + [TestMethod] + public async Task SaveOptionsAsync_ValidOptions_ReturnsSuccessAsync() + { + // Arrange + var options = new DownloadOptions() + { + ProjectFilename = "Test.ace", + CuesheetFilename = "Test.cue" + }; + Boolean eventRaised = false; + _service.OptionSaved += delegate + { + eventRaised = true; + }; + // Act + var result = await _service.SaveOptionsAsync(options); + // Assert + Assert.IsTrue(result.IsSuccess); + Assert.IsTrue(eventRaised); + } + + [TestMethod] + public async Task SaveOptionsAsync_InvalidOptions_ReturnsFailureAsync() + { + // Arrange + var options = new DownloadOptions() + { + CuesheetFilename = null, + ProjectFilename = null + }; + Boolean eventRaised = false; + _service.OptionSaved += delegate + { + eventRaised = true; + }; + // Act + var result = await _service.SaveOptionsAsync(options); + // Assert + Assert.IsFalse(result.IsSuccess); + Assert.IsFalse(eventRaised); + } + } +} diff --git a/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs b/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs index a8205c0f..6412aa39 100644 --- a/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs +++ b/AudioCuesheetEditor.Tests/Services/UI/ApplicationOptionsTimeSpanParserTests.cs @@ -13,10 +13,10 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.Options; using AudioCuesheetEditor.Model.Utility; +using AudioCuesheetEditor.Services.Options; using AudioCuesheetEditor.Services.UI; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; diff --git a/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs b/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs index 91151f28..93a7028a 100644 --- a/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs +++ b/AudioCuesheetEditor.Tests/Services/UI/SessionStateContainerTests.cs @@ -13,10 +13,10 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.IO.Import; using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Services.Options; using AudioCuesheetEditor.Services.UI; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; diff --git a/AudioCuesheetEditor/Program.cs b/AudioCuesheetEditor/Program.cs index 0f2f478f..52932a92 100644 --- a/AudioCuesheetEditor/Program.cs +++ b/AudioCuesheetEditor/Program.cs @@ -14,13 +14,13 @@ //along with Foobar. If not, see //. using AudioCuesheetEditor; -using AudioCuesheetEditor.Data.Options; -using AudioCuesheetEditor.Data.Services; using AudioCuesheetEditor.Extensions; using AudioCuesheetEditor.Model.Options; using AudioCuesheetEditor.Services.Audio; using AudioCuesheetEditor.Services.AudioCuesheet; using AudioCuesheetEditor.Services.IO; +using AudioCuesheetEditor.Services.Options; +using AudioCuesheetEditor.Services.Provider; using AudioCuesheetEditor.Services.UI; using AudioCuesheetEditor.Services.Validation; using BlazorDownloadFile; diff --git a/AudioCuesheetEditor/Services/IO/TextImportService.cs b/AudioCuesheetEditor/Services/IO/TextImportService.cs index e196ca0f..39bca6d0 100644 --- a/AudioCuesheetEditor/Services/IO/TextImportService.cs +++ b/AudioCuesheetEditor/Services/IO/TextImportService.cs @@ -14,13 +14,13 @@ //along with Foobar. If not, see //. -using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.AudioCuesheet.Import; using AudioCuesheetEditor.Model.IO.Audio; using AudioCuesheetEditor.Model.IO.Import; using AudioCuesheetEditor.Model.Options; using AudioCuesheetEditor.Model.Utility; +using AudioCuesheetEditor.Services.Options; using System.Reflection; using System.Text; using System.Text.RegularExpressions; diff --git a/AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs b/AudioCuesheetEditor/Services/Options/ILocalStorageOptionsProvider.cs similarity index 82% rename from AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs rename to AudioCuesheetEditor/Services/Options/ILocalStorageOptionsProvider.cs index e96fee9d..db629e4b 100644 --- a/AudioCuesheetEditor/Data/Options/ILocalStorageOptionsProvider.cs +++ b/AudioCuesheetEditor/Services/Options/ILocalStorageOptionsProvider.cs @@ -16,14 +16,14 @@ using AudioCuesheetEditor.Model.Options; using System.Linq.Expressions; -namespace AudioCuesheetEditor.Data.Options +namespace AudioCuesheetEditor.Services.Options { public interface ILocalStorageOptionsProvider { event EventHandler? OptionSaved; Task GetOptionsAsync() where T : IOptions; - Task SaveOptionsAsync(IOptions options); - Task SaveOptionsValueAsync(Expression> propertyExpression, object? value) where T : class, IOptions, new(); + Task SaveOptionsAsync(IOptions options); + Task SaveOptionsValueAsync(Expression> propertyExpression, object? value) where T : class, IOptions, new(); Task SaveNestedOptionValueAsync(Expression> nestedPropertyExpression, Expression> valuePropertyExpression, TValue value) where T : class, IOptions, new(); } } diff --git a/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs b/AudioCuesheetEditor/Services/Options/LocalStorageOptionsProvider.cs similarity index 80% rename from AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs rename to AudioCuesheetEditor/Services/Options/LocalStorageOptionsProvider.cs index 10a70da7..aa46c044 100644 --- a/AudioCuesheetEditor/Data/Options/LocalStorageOptionsProvider.cs +++ b/AudioCuesheetEditor/Services/Options/LocalStorageOptionsProvider.cs @@ -15,20 +15,21 @@ //. using AudioCuesheetEditor.Model.Entity; using AudioCuesheetEditor.Model.Options; +using Microsoft.Extensions.Localization; using Microsoft.JSInterop; using System.Linq.Expressions; using System.Reflection; using System.Text.Json; -namespace AudioCuesheetEditor.Data.Options +namespace AudioCuesheetEditor.Services.Options { - public class LocalStorageOptionsProvider(IJSRuntime jsRuntime): ILocalStorageOptionsProvider + public class LocalStorageOptionsProvider(IJSRuntime jsRuntime, IStringLocalizer localizer) : ILocalStorageOptionsProvider { public event EventHandler? OptionSaved; private readonly IJSRuntime _jsRuntime = jsRuntime; - - private readonly JsonSerializerOptions SerializerOptions = new() + private readonly IStringLocalizer _localizer = localizer; + private readonly JsonSerializerOptions _serializerOptions = new() { DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull }; @@ -61,22 +62,21 @@ public async Task GetOptionsAsync() where T : IOptions } } - public async Task SaveOptionsAsync(IOptions options) + public async Task SaveOptionsAsync(IOptions options) { - bool saveOptions = true; if (options is IValidateable validateable) { - saveOptions = validateable.Validate().Status != ValidationStatus.Error; - } - if (saveOptions) - { - var optionsJson = JsonSerializer.Serialize(options, SerializerOptions); - await _jsRuntime.InvokeVoidAsync("AppSettings.set", options.GetType().Name, optionsJson); - OptionSaved?.Invoke(this, options); + var validationResult = validateable.Validate(); + if (validationResult.Status == ValidationStatus.Error) + { + return Result.Failure(new Error(ErrorType.ValidationFailed, string.Join(Environment.NewLine, validationResult.ValidationMessages.Select(x => x.GetMessageLocalized(_localizer))))); + } } + await WriteOptionsAsync(options); + return Result.Success(); } - public async Task SaveOptionsValueAsync(Expression> propertyExpression, object? value) where T : class, IOptions, new() + public async Task SaveOptionsValueAsync(Expression> propertyExpression, object? value) where T : class, IOptions, new() { var options = await GetOptionsAsync(); PropertyInfo? propertyInfo = null; @@ -99,7 +99,7 @@ public async Task SaveOptionsAsync(IOptions options) { throw new ArgumentException("The provided expression does not reference a valid property."); } - await SaveOptionsAsync(options); + return await SaveOptionsAsync(options); } public async Task SaveNestedOptionValueAsync(Expression> nestedPropertyExpression, Expression> valuePropertyExpression, TValue value) where T : class, IOptions, new() @@ -152,5 +152,12 @@ public async Task SaveOptionsAsync(IOptions options) return propertyInfo; } + + async Task WriteOptionsAsync(IOptions options) + { + var optionsJson = JsonSerializer.Serialize(options, _serializerOptions); + await _jsRuntime.InvokeVoidAsync("AppSettings.set", options.GetType().Name, optionsJson); + OptionSaved?.Invoke(this, options); + } } } diff --git a/AudioCuesheetEditor/Data/Services/MusicBrainzDataProvider.cs b/AudioCuesheetEditor/Services/Provider/MusicBrainzDataProvider.cs similarity index 99% rename from AudioCuesheetEditor/Data/Services/MusicBrainzDataProvider.cs rename to AudioCuesheetEditor/Services/Provider/MusicBrainzDataProvider.cs index a7350ff9..440cd292 100644 --- a/AudioCuesheetEditor/Data/Services/MusicBrainzDataProvider.cs +++ b/AudioCuesheetEditor/Services/Provider/MusicBrainzDataProvider.cs @@ -18,7 +18,7 @@ using MetaBrainz.MusicBrainz.Interfaces.Searches; using System.Reflection; -namespace AudioCuesheetEditor.Data.Services +namespace AudioCuesheetEditor.Services.Provider { public class MusicBrainzArtist { diff --git a/AudioCuesheetEditor/Services/UI/ApplicationOptionsTimeSpanParser.cs b/AudioCuesheetEditor/Services/UI/ApplicationOptionsTimeSpanParser.cs index d23c44ac..9a80bd15 100644 --- a/AudioCuesheetEditor/Services/UI/ApplicationOptionsTimeSpanParser.cs +++ b/AudioCuesheetEditor/Services/UI/ApplicationOptionsTimeSpanParser.cs @@ -13,9 +13,9 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.Options; using AudioCuesheetEditor.Model.Utility; +using AudioCuesheetEditor.Services.Options; using System.Linq.Expressions; using System.Reflection; diff --git a/AudioCuesheetEditor/Services/UI/AutocompleteManager.cs b/AudioCuesheetEditor/Services/UI/AutocompleteManager.cs index ea45da16..df9b58a7 100644 --- a/AudioCuesheetEditor/Services/UI/AutocompleteManager.cs +++ b/AudioCuesheetEditor/Services/UI/AutocompleteManager.cs @@ -13,7 +13,7 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Data.Services; +using AudioCuesheetEditor.Services.Provider; namespace AudioCuesheetEditor.Services.UI { diff --git a/AudioCuesheetEditor/Services/UI/LocalizationService.cs b/AudioCuesheetEditor/Services/UI/LocalizationService.cs index f158f979..1aeb8b2e 100644 --- a/AudioCuesheetEditor/Services/UI/LocalizationService.cs +++ b/AudioCuesheetEditor/Services/UI/LocalizationService.cs @@ -13,8 +13,8 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Services.Options; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; using System.Globalization; diff --git a/AudioCuesheetEditor/Services/UI/SessionStateContainer.cs b/AudioCuesheetEditor/Services/UI/SessionStateContainer.cs index 68418a8f..97cb8361 100644 --- a/AudioCuesheetEditor/Services/UI/SessionStateContainer.cs +++ b/AudioCuesheetEditor/Services/UI/SessionStateContainer.cs @@ -13,11 +13,11 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Data.Options; using AudioCuesheetEditor.Model.AudioCuesheet; using AudioCuesheetEditor.Model.IO.Audio; using AudioCuesheetEditor.Model.IO.Import; using AudioCuesheetEditor.Model.Options; +using AudioCuesheetEditor.Services.Options; namespace AudioCuesheetEditor.Services.UI { diff --git a/AudioCuesheetEditor/Shared/BaseLocalizedComponent.cs b/AudioCuesheetEditor/Shared/BaseLocalizedComponent.cs index ddb56da7..17077ea4 100644 --- a/AudioCuesheetEditor/Shared/BaseLocalizedComponent.cs +++ b/AudioCuesheetEditor/Shared/BaseLocalizedComponent.cs @@ -13,7 +13,7 @@ //You should have received a copy of the GNU General Public License //along with Foobar. If not, see //. -using AudioCuesheetEditor.Data.Options; +using AudioCuesheetEditor.Services.Options; using AudioCuesheetEditor.Services.UI; using Microsoft.AspNetCore.Components; diff --git a/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor b/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor index 374ef511..85ca96bb 100644 --- a/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor +++ b/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor @@ -24,49 +24,35 @@ along with Foobar. If not, see - @if (String.IsNullOrEmpty(GetGenerationValidationMessages()) == false) + @if (String.IsNullOrEmpty(_validationErrorMessage) == false) { @_localizer["Export is currently not possible:"] - @((MarkupString)GetGenerationValidationMessages()!) + @((MarkupString)_validationErrorMessage) } -
- - - @_localizer["Name"] - @_localizer["Content"] - - - @context.Name - @{ - var ariaLabel = $"Download-{context.Name}"; - - } - - -
+ Error="String.IsNullOrEmpty(_fileNameValidationErrorMessage) == false" ErrorText="@_fileNameValidationErrorMessage" />
+ + @_localizer["Download"] +
@code { - //TODO: Get validation messages only once during rendering and use the reference for better performance - //TODO: Will be a single file when #548 gets done - IEnumerable _exportfiles = []; DownloadOptions? _options; + String? _fileNameValidationErrorMessage = null; + String? _validationErrorMessage = null; + + [CascadingParameter] + private IMudDialogInstance? MudDialog { get; set; } protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); _options = await base.LocalStorageOptionsProvider.GetOptionsAsync(); base.LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; - var result = _cuesheetExportService.GenerateExportfile(_options?.CuesheetFilename); - if (result.IsSuccess) - { - _exportfiles = [result.Value!]; - } + SetValidationErrors(); } protected override void Dispose(bool disposing) @@ -75,38 +61,45 @@ along with Foobar. If not, see base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; } - String? GetValidationErrorMessage() + async Task OnDownloadClick() { - String? validationErrorMessage = null; - if (_options != null) + MudDialog?.Close(); + var generateExportResult = _cuesheetExportService.GenerateExportfile(_options!.CuesheetFilename); + if (generateExportResult.IsSuccess == false) { - var validationMessages = _validationService.Validate(_options, nameof(DownloadOptions.CuesheetFilename)); - if (validationMessages.Count() > 0) - { - validationErrorMessage = String.Join(Environment.NewLine, validationMessages); - } + throw new InvalidOperationException($"Could not generate export file: {generateExportResult.Error?.Message}"); } - return validationErrorMessage; + var exportfile = generateExportResult.Value!; + await _blazorDownloadFileService.DownloadFileFromText(exportfile.Name, exportfile.Content, System.Text.Encoding.Default, "text/plain"); } - String? GetGenerationValidationMessages() + void SetValidationErrors() { - String? validationErrorMessage = null; + _validationErrorMessage = null; var result = _cuesheetExportService.CanGenerateExportfile(_options?.CuesheetFilename); if (result.IsSuccess == false) { - validationErrorMessage = result.Error?.Message.Replace(Environment.NewLine, "
"); + _validationErrorMessage = result.Error?.Message.Replace(Environment.NewLine, "
"); } - return validationErrorMessage; + SetFileNameValidationError(); } - async Task CuesheetFilenameChanged(string newFilename) + void SetFileNameValidationError() { - await LocalStorageOptionsProvider.SaveOptionsValueAsync(x => x.CuesheetFilename, newFilename); - var result = _cuesheetExportService.GenerateExportfile(_options?.CuesheetFilename); - if (result.IsSuccess) + _fileNameValidationErrorMessage = null; + var validationMessages = _validationService.Validate(_options, nameof(DownloadOptions.CuesheetFilename)); + if (validationMessages.Count() > 0) + { + _fileNameValidationErrorMessage = String.Join(Environment.NewLine, validationMessages); + } + } + + async Task CuesheetFilenameChanged(string? newFilename) + { + var result = await LocalStorageOptionsProvider.SaveOptionsValueAsync(x => x.CuesheetFilename, newFilename); + if (result.IsSuccess == false) { - _exportfiles = [result.Value!]; + _fileNameValidationErrorMessage = result.Error?.Message; } } @@ -115,6 +108,7 @@ along with Foobar. If not, see if (option is DownloadOptions downloadOptions) { _options = downloadOptions; + SetValidationErrors(); StateHasChanged(); } } diff --git a/AudioCuesheetEditor/_Imports.razor b/AudioCuesheetEditor/_Imports.razor index da2c547d..41555c08 100644 --- a/AudioCuesheetEditor/_Imports.razor +++ b/AudioCuesheetEditor/_Imports.razor @@ -31,14 +31,13 @@ @using AudioCuesheetEditor.Model.Options @using AudioCuesheetEditor.Extensions @using AudioCuesheetEditor.Model.UI -@using AudioCuesheetEditor.Data.Options @using AudioCuesheetEditor.Model.Utility -@using AudioCuesheetEditor.Data.Services @using AudioCuesheetEditor.Services.IO @using AudioCuesheetEditor.Services.UI @using AudioCuesheetEditor.Services.Validation @using AudioCuesheetEditor.Services.Audio @using AudioCuesheetEditor.Services.AudioCuesheet +@using AudioCuesheetEditor.Services.Provider @using Microsoft.Extensions.Logging @using Microsoft.Extensions.Localization @using BlazorDownloadFile