diff --git a/.github/workflows/build_pipeline.yml b/.github/workflows/build_pipeline.yml index 72c301ec..f6948c68 100644 --- a/.github/workflows/build_pipeline.yml +++ b/.github/workflows/build_pipeline.yml @@ -24,7 +24,7 @@ jobs: dotnet-version: 9.0.x - name: Install wasm-tools - run: dotnet workload install wasm-tools + run: dotnet workload install wasm-tools-net9 - name: Build release run: dotnet publish AudioCuesheetEditor --configuration Release --output release diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 3188bb04..1fe3a46b 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -21,7 +21,7 @@ jobs: dotnet-version: 9.0.x - name: Install wasm-tools - run: dotnet workload install wasm-tools + run: dotnet workload install wasm-tools-net9 - name: Build & Install run: dotnet build diff --git a/AudioCuesheetEditor.End2EndTests/Models/DetailView.cs b/AudioCuesheetEditor.End2EndTests/Models/DetailView.cs index a9df3024..473d78a4 100644 --- a/AudioCuesheetEditor.End2EndTests/Models/DetailView.cs +++ b/AudioCuesheetEditor.End2EndTests/Models/DetailView.cs @@ -85,6 +85,11 @@ internal async Task SelectTracksAsync(IEnumerable trackTablePositions, Bool } } + internal async Task EditSelectedTracksModalAsync() + { + await _page.GetByRole(AriaRole.Button, new() { Name = "Edit selected tracks" }).ClickAsync(); + } + internal async Task EditTracksModalAsync(string artist, string title, string end, IEnumerable flagsToSelect) { await _page.GetByRole(AriaRole.Button, new() { Name = "Edit selected tracks" }).ClickAsync(); diff --git a/AudioCuesheetEditor.End2EndTests/Models/ViewModes.cs b/AudioCuesheetEditor.End2EndTests/Models/ViewModes.cs new file mode 100644 index 00000000..f1a69508 --- /dev/null +++ b/AudioCuesheetEditor.End2EndTests/Models/ViewModes.cs @@ -0,0 +1,38 @@ +//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 Microsoft.Playwright; +using System.Text.RegularExpressions; + +namespace AudioCuesheetEditor.End2EndTests.Models +{ + internal partial class ViewModes(IPage page, bool mobile) + { + private readonly IPage _page = page; + private readonly bool _isMobile = mobile; + + internal async Task SwitchView(string viewMode) + { + if (_isMobile) + { + await _page.GetByRole(AriaRole.Button).Filter(new() { HasTextRegex = ViewModeForward() }).Nth(4).ClickAsync(); + } + await _page.GetByText(viewMode).ClickAsync(); + } + + [GeneratedRegex("^$")] + internal static partial Regex ViewModeForward(); + } +} diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/RecordTest.cs b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/RecordTest.cs index 33ac4a6c..8bb7d16d 100644 --- a/AudioCuesheetEditor.End2EndTests/Tests/Desktop/RecordTest.cs +++ b/AudioCuesheetEditor.End2EndTests/Tests/Desktop/RecordTest.cs @@ -25,6 +25,8 @@ public class RecordTest : PlaywrightTestBase public async Task Record_ShouldRecordTracks_WhenTracksAdded() { var recordView = new RecordView(TestPage); + var viewModes = new ViewModes(TestPage, DeviceName != null); + var detailView = new DetailView(TestPage, DeviceName != null); await recordView.GotoAsync(); await recordView.StartRecordingAsync(); await recordView.AddRecordingTrackAsync("Test Track 1 Artist", "Test Track 1 Title"); @@ -34,6 +36,10 @@ public async Task Record_ShouldRecordTracks_WhenTracksAdded() await Expect(TestPage.GetByRole(AriaRole.Cell, new() { Name = "Test Track 1 Title Clear" })).ToBeVisibleAsync(); await Expect(TestPage.GetByRole(AriaRole.Cell, new() { Name = "Test Track 2 Artist Clear" })).ToBeVisibleAsync(); await Expect(TestPage.GetByRole(AriaRole.Cell, new() { Name = "Test Track 2 Title Clear" })).ToBeVisibleAsync(); + await viewModes.SwitchView("Detail view"); + await detailView.SelectTracksAsync([1]); + await detailView.EditSelectedTracksModalAsync(); + await Expect(TestPage.GetByRole(AriaRole.Checkbox, new() { Name = "Link to previous track" })).ToBeCheckedAsync(); } } } diff --git a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/RecordTestSmartphone.cs b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/RecordTestSmartphone.cs index acb151b0..e86df736 100644 --- a/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/RecordTestSmartphone.cs +++ b/AudioCuesheetEditor.End2EndTests/Tests/Smartphone/RecordTestSmartphone.cs @@ -26,6 +26,8 @@ public class RecordTestSmartphone : PlaywrightTestBase public async Task Record_ShouldRecordTracks_WhenTracksAdded() { var recordView = new RecordView(TestPage); + var viewModes = new ViewModes(TestPage, DeviceName != null); + var detailView = new DetailView(TestPage, DeviceName != null); await recordView.GotoAsync(); await recordView.StartRecordingAsync(); await recordView.AddRecordingTrackAsync("Test Track 1 Artist", "Test Track 1 Title"); @@ -35,6 +37,10 @@ public async Task Record_ShouldRecordTracks_WhenTracksAdded() await Expect(TestPage.GetByRole(AriaRole.Cell, new() { Name = "Test Track 1 Title Clear" })).ToBeVisibleAsync(); await Expect(TestPage.GetByRole(AriaRole.Cell, new() { Name = "Test Track 2 Artist Clear" })).ToBeVisibleAsync(); await Expect(TestPage.GetByRole(AriaRole.Cell, new() { Name = "Test Track 2 Title Clear" })).ToBeVisibleAsync(); + await viewModes.SwitchView("Detail view"); + await detailView.SelectTracksAsync([1]); + await detailView.EditSelectedTracksModalAsync(); + await Expect(TestPage.GetByRole(AriaRole.Checkbox, new() { Name = "Link to previous track" })).ToBeCheckedAsync(); } } } diff --git a/AudioCuesheetEditor/Pages/Index.razor b/AudioCuesheetEditor/Pages/Index.razor index aaa79b27..64a3150d 100644 --- a/AudioCuesheetEditor/Pages/Index.razor +++ b/AudioCuesheetEditor/Pages/Index.razor @@ -21,7 +21,6 @@ along with Foobar. If not, see @inherits BaseLocalizedComponent @inject IStringLocalizer _localizer -@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @@ -44,9 +43,9 @@ along with Foobar. If not, see protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - options = await _localStorageOptionsProvider.GetOptionsAsync(); + options = await base.LocalStorageOptionsProvider.GetOptionsAsync(); currentViewmode = options.ActiveTab; - _localStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; + base.LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; } protected override void Dispose(bool disposing) @@ -54,7 +53,7 @@ along with Foobar. If not, see base.Dispose(disposing); if (disposing) { - _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; + base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; } } diff --git a/AudioCuesheetEditor/Shared/Dialogs/DownloadProjectfileDialog.razor b/AudioCuesheetEditor/Shared/Dialogs/DownloadProjectfileDialog.razor index ecd7cd04..a5ce7d9a 100644 --- a/AudioCuesheetEditor/Shared/Dialogs/DownloadProjectfileDialog.razor +++ b/AudioCuesheetEditor/Shared/Dialogs/DownloadProjectfileDialog.razor @@ -21,7 +21,6 @@ along with Foobar. If not, see @inject ISessionStateContainer _sessionStateContainer @inject IBlazorDownloadFileService _blazorDownloadFileService @inject ValidationService _validationService -@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @@ -42,14 +41,14 @@ along with Foobar. If not, see protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - options = await _localStorageOptionsProvider.GetOptionsAsync(); - _localStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; + options = await base.LocalStorageOptionsProvider.GetOptionsAsync(); + base.LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; } protected override void Dispose(bool disposing) { base.Dispose(disposing); - _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; + base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; } async Task DownloadProjectClick() diff --git a/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor b/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor index cd38b721..be6613a6 100644 --- a/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor +++ b/AudioCuesheetEditor/Shared/Dialogs/GenerateCuesheetDialog.razor @@ -22,7 +22,6 @@ along with Foobar. If not, see @inject ValidationService _validationService @inject IBlazorDownloadFileService _blazorDownloadFileService @inject CuesheetExportService _cuesheetExportService -@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @@ -64,15 +63,15 @@ along with Foobar. If not, see protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - options = await _localStorageOptionsProvider.GetOptionsAsync(); - _localStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; + options = await base.LocalStorageOptionsProvider.GetOptionsAsync(); + base.LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; exportfiles = _cuesheetExportService.GenerateExportfiles(options?.CuesheetFilename); } protected override void Dispose(bool disposing) { base.Dispose(disposing); - _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; + base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; } String? GetValidationErrorMessage() diff --git a/AudioCuesheetEditor/Shared/Dialogs/SettingsDialog.razor b/AudioCuesheetEditor/Shared/Dialogs/SettingsDialog.razor index 3d65b4b2..b512dbe6 100644 --- a/AudioCuesheetEditor/Shared/Dialogs/SettingsDialog.razor +++ b/AudioCuesheetEditor/Shared/Dialogs/SettingsDialog.razor @@ -19,7 +19,6 @@ along with Foobar. If not, see @inject IStringLocalizer _localizer @inject ValidationService _validationService -@inject ILocalStorageOptionsProvider _localStorageOptionsProvider diff --git a/AudioCuesheetEditor/Shared/Dialogs/StartRecordCountdownDialog.razor b/AudioCuesheetEditor/Shared/Dialogs/StartRecordCountdownDialog.razor index f8a5507d..b39123d6 100644 --- a/AudioCuesheetEditor/Shared/Dialogs/StartRecordCountdownDialog.razor +++ b/AudioCuesheetEditor/Shared/Dialogs/StartRecordCountdownDialog.razor @@ -18,7 +18,6 @@ along with Foobar. If not, see @inherits BaseLocalizedComponent @inject IStringLocalizer _localizer -@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @@ -43,14 +42,14 @@ along with Foobar. If not, see protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - options = await _localStorageOptionsProvider.GetOptionsAsync(); - _localStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; + options = await base.LocalStorageOptionsProvider.GetOptionsAsync(); + base.LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; } protected override void Dispose(bool disposing) { base.Dispose(disposing); - _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; + base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; } void LocalStorageOptionsProvider_OptionSaved(object? sender, IOptions option) diff --git a/AudioCuesheetEditor/Shared/Import/Importprofiles.razor b/AudioCuesheetEditor/Shared/Import/Importprofiles.razor index 6ba50c3b..57e6a842 100644 --- a/AudioCuesheetEditor/Shared/Import/Importprofiles.razor +++ b/AudioCuesheetEditor/Shared/Import/Importprofiles.razor @@ -21,7 +21,6 @@ along with Foobar. If not, see @inject IStringLocalizer _localizer @inject ValidationService _validationService @inject IDialogService _dialogService -@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @if (importOptions != null) { @@ -96,14 +95,14 @@ along with Foobar. If not, see protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - importOptions = await _localStorageOptionsProvider.GetOptionsAsync(); - _localStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; + importOptions = await base.LocalStorageOptionsProvider.GetOptionsAsync(); + base.LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; } protected override void Dispose(bool disposing) { base.Dispose(disposing); - _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; + base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; } void AppendPlaceholderToTextField(MudTextField? mudTextField, string placeholder) diff --git a/AudioCuesheetEditor/Shared/Record/AddTrack.razor b/AudioCuesheetEditor/Shared/Record/AddTrack.razor index bf6321db..ba00b751 100644 --- a/AudioCuesheetEditor/Shared/Record/AddTrack.razor +++ b/AudioCuesheetEditor/Shared/Record/AddTrack.razor @@ -107,6 +107,17 @@ along with Foobar. If not, see } } + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + applicationOptions = await base.LocalStorageOptionsProvider.GetOptionsAsync(); + base.LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; + currentRecordingTrack = new() + { + IsLinkedToPreviousTrack = applicationOptions!.DefaultIsLinkedToPreviousTrack + }; + } + protected override void Dispose(bool disposing) { base.Dispose(disposing); @@ -114,11 +125,13 @@ along with Foobar. If not, see { Cuesheet.IsRecordingChanged -= Cuesheet_IsRecordingChanged; } + base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; } - Track currentRecordingTrack = new Track(); + Track currentRecordingTrack = new(); MudAutocomplete? artistInput; MudAutocomplete? titleInput; + ApplicationOptions? applicationOptions; String? GetValidationErrorMessage(object model, string propertyName) { @@ -136,7 +149,10 @@ along with Foobar. If not, see if (Cuesheet?.IsRecording == true) { Cuesheet?.AddTrack(currentRecordingTrack); - currentRecordingTrack = new(); + currentRecordingTrack = new() + { + IsLinkedToPreviousTrack = applicationOptions!.DefaultIsLinkedToPreviousTrack + }; if (artistInput != null) { await artistInput.FocusAsync(); @@ -150,4 +166,13 @@ along with Foobar. If not, see artistInput?.ClearAsync(); titleInput?.ClearAsync(); } + + void LocalStorageOptionsProvider_OptionSaved(object? sender, IOptions option) + { + if (option is ApplicationOptions applicationOption) + { + applicationOptions = applicationOption; + StateHasChanged(); + } + } } \ No newline at end of file diff --git a/AudioCuesheetEditor/Shared/TrackList/TrackList.razor b/AudioCuesheetEditor/Shared/TrackList/TrackList.razor index 91109481..fe1c94a8 100644 --- a/AudioCuesheetEditor/Shared/TrackList/TrackList.razor +++ b/AudioCuesheetEditor/Shared/TrackList/TrackList.razor @@ -23,7 +23,6 @@ along with Foobar. If not, see @inject AutocompleteManager _autocompleteManager @inject DialogManager _editTrackModalManager @inject ValidationService _validationService -@inject ILocalStorageOptionsProvider _localStorageOptionsProvider @inject PlaybackService _playbackService @inject IDialogService _dialogService @inject ISessionStateContainer _sessionStateContainer @@ -50,7 +49,7 @@ along with Foobar. If not, see MoveTracksUpDisabled="!(Cuesheet?.MoveTracksPossible(selectedTracks, MoveDirection.Up) == true)" MoveTracksUpClicked="() => Cuesheet?.MoveTracks(selectedTracks, MoveDirection.Up)" MoveTracksDownDisabled="!(Cuesheet?.MoveTracksPossible(selectedTracks, MoveDirection.Down) == true)" MoveTracksDownClicked="() => Cuesheet?.MoveTracks(selectedTracks, MoveDirection.Down)" CopySelectedTracksDisabled="selectedTracks.Count != 1" CopySelectedTracksClicked="() => CopyTrackClicked()" - FixedHeader="applicationOptions?.FixedTracksTableHeader == true" FixedHeaderClicked="() => _localStorageOptionsProvider.SaveOptionsValueAsync(x => x.FixedTracksTableHeader, !applicationOptions?.FixedTracksTableHeader)" /> + FixedHeader="applicationOptions?.FixedTracksTableHeader == true" FixedHeaderClicked="() => base.LocalStorageOptionsProvider.SaveOptionsValueAsync(x => x.FixedTracksTableHeader, !applicationOptions?.FixedTracksTableHeader)" /> break; } @@ -162,7 +161,7 @@ along with Foobar. If not, see protected override void Dispose(bool disposing) { base.Dispose(disposing); - _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; + base.LocalStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; _sessionStateContainer.ImportCuesheetChanged -= SessionStateContainer_ImportCuesheetChanged; _sessionStateContainer.CuesheetChanged -= SessionStateContainer_CuesheetChanged; } @@ -170,8 +169,8 @@ along with Foobar. If not, see protected override async Task OnInitializedAsync() { await base.OnInitializedAsync(); - applicationOptions = await _localStorageOptionsProvider.GetOptionsAsync(); - _localStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; + applicationOptions = await base.LocalStorageOptionsProvider.GetOptionsAsync(); + base.LocalStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; _sessionStateContainer.ImportCuesheetChanged += SessionStateContainer_ImportCuesheetChanged; _sessionStateContainer.CuesheetChanged += SessionStateContainer_CuesheetChanged; }