From add2c80f9b30c52fede40b4e09502492e20e8435 Mon Sep 17 00:00:00 2001 From: NeoCoderMatrix86 <40752681+NeoCoderMatrix86@users.noreply.github.com> Date: Mon, 11 Nov 2024 14:39:02 +0100 Subject: [PATCH] More components for tracklist --- AudioCuesheetEditor/Shared/TrackList.razor | 805 ------------------ .../Shared/TrackList/TrackList.razor | 489 +++++++++++ .../Shared/TrackList/TrackListItem.razor | 399 +++++++++ .../Shared/TrackList/TrackSelection.razor | 59 ++ AudioCuesheetEditor/_Imports.razor | 1 + 5 files changed, 948 insertions(+), 805 deletions(-) delete mode 100644 AudioCuesheetEditor/Shared/TrackList.razor create mode 100644 AudioCuesheetEditor/Shared/TrackList/TrackList.razor create mode 100644 AudioCuesheetEditor/Shared/TrackList/TrackListItem.razor create mode 100644 AudioCuesheetEditor/Shared/TrackList/TrackSelection.razor diff --git a/AudioCuesheetEditor/Shared/TrackList.razor b/AudioCuesheetEditor/Shared/TrackList.razor deleted file mode 100644 index c3fd2e20..00000000 --- a/AudioCuesheetEditor/Shared/TrackList.razor +++ /dev/null @@ -1,805 +0,0 @@ - - -@implements IDisposable - -@inject ITextLocalizer _localizer -@inject SessionStateContainer _sessionStateContainer -@inject ILocalStorageOptionsProvider _localStorageOptionsProvider -@inject TraceChangeManager _traceChangeManager -@inject ILogger _logger -@inject ITextLocalizerService _localizationService -@inject MusicBrainzDataProvider _musicBrainzDataProvider -@inject ITextLocalizer _validationMessageLocalizer -@inject ApplicationOptionsTimeSpanParser _applicationOptionsTimeSpanParser - - - @if (_sessionStateContainer.CurrentViewMode == ViewMode.ViewModeFull) - { - var validationResult = Cuesheet?.Validate(x => x.Tracks); - - - @_localizer["Validation errors"] - - @if (validationResult?.ValidationMessages != null) - { - @foreach(var message in validationResult.ValidationMessages) - { - @message.GetMessageLocalized(_validationMessageLocalizer) - } - } - - - - - - - - - @if (TrackSelectionVisible == false) - { - - - - } - else - { - - - - } - - - - - - - - - - - - - -
- } - - - - - @if ((_sessionStateContainer.CurrentViewMode == ViewMode.ViewModeFull) && (TrackSelectionVisible)) - { - -
- @if (Cuesheet?.Tracks.Count > 0) - { - - - - } - @_localizer["Selection"] -
-
- } - @switch (_sessionStateContainer.CurrentViewMode) - { - case ViewMode.ViewModeFull: - case ViewMode.ViewModeImport: - @_localizer["Controls"] - break; - case ViewMode.ViewModeRecord: - @_localizer["Controls"] - break; - } - # - @_localizer["Artist"] - @_localizer["Title"] - @_localizer["Begin"] - @_localizer["End"] - @_localizer["Length"] -
-
- - @if (Cuesheet != null) - { - - @if ((track != Cuesheet?.Tracks.FirstOrDefault()) && (_sessionStateContainer.CurrentViewMode == ViewMode.ViewModeFull)) - { - - - @if (track.IsLinkedToPreviousTrack) - { - - } - else - { - - } - - - } - - @if (TrackSelectionVisible) - { - - - - - - } - @switch (_sessionStateContainer.CurrentViewMode) - { - case ViewMode.ViewModeRecord: - - - - @track.Position - break; - case ViewMode.ViewModeFull: - case ViewMode.ViewModeImport: - - - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
-
- - - - - - - - - - break; - } - - - - - @if (context.Item.Disambiguation != null) - { - @String.Format("{0} ({1})", context.Text, context.Item.Disambiguation) - } - else - { - @context.Text - } - - - - - - - - - @if (context.Item.Disambiguation != null) - { - @String.Format("{0} ({1})", context.Text, context.Item.Disambiguation) - } - else - { - @context.Text - } - - - - - @switch (_sessionStateContainer.CurrentViewMode) - { - case ViewMode.ViewModeRecord: - @track.Begin - @track.End - @track.Length - break; - case ViewMode.ViewModeFull: - case ViewMode.ViewModeImport: - -
- @if (Cuesheet?.GetSectionAtTrack(track) != null) - { - - - - - - - - } - - - - - - - - - -
-
- - - - - - - - - - - - - - - - - - - - - - - break; - } -
-
- } -
-
-
- - - - -@code { - ModalDialog? modalDialog; - EditTrackModal? modalTrackEdit; - List selectedTracks = new(); - IEnumerable? autocompleteTrackArtists; - IEnumerable? autocompleteTrackTitles; - Boolean _trackSelectionVisible = false; - //Saved locally because of performance (do not load everytime something is edited) - ApplicationOptions? applicationOptions; - Validations? validations; - Boolean revalidate = false; - List TracksAttachedToValidateablePropertyChanged = new(); - - [Parameter] - public AudioPlayer? AudioPlayer { get; set; } - - public Cuesheet? Cuesheet - { - get - { - Cuesheet? cuesheet; - switch (_sessionStateContainer.CurrentViewMode) - { - case ViewMode.ViewModeImport: - cuesheet = _sessionStateContainer.ImportCuesheet; - break; - default: - cuesheet = _sessionStateContainer.Cuesheet; - break; - } - return cuesheet; - } - } - - public Boolean TrackSelectionVisible - { - get => _trackSelectionVisible; - set - { - _trackSelectionVisible = value; - selectedTracks = new(); - } - } - - public void Dispose() - { - _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; - _localStorageOptionsProvider.OptionSaved -= LocalStorageOptionsProvider_OptionSaved; - _sessionStateContainer.CuesheetChanged -= SessionStateContainer_CuesheetChanged; - _sessionStateContainer.ImportCuesheetChanged -= SessionStateContainer_ImportCuesheetChanged; - _traceChangeManager.UndoDone -= TraceChangeManager_UndoDone; - _traceChangeManager.RedoDone -= TraceChangeManager_RedoDone; - _sessionStateContainer.Cuesheet.TrackRemoved -= Cuesheet_TrackRemoved; - _sessionStateContainer.Cuesheet.TrackAdded -= Cuesheet_TrackAdded; - DetachTrackFromValidateablePropertyChanged(); - DetachCuesheetFromSplitPointsAddedRemoved(); - } - - protected override async Task OnInitializedAsync() - { - _logger.LogDebug("OnInitializedAsync"); - - _localizationService.LocalizationChanged += LocalizationService_LocalizationChanged; - - applicationOptions = await _localStorageOptionsProvider.GetOptions(); - _localStorageOptionsProvider.OptionSaved += LocalStorageOptionsProvider_OptionSaved; - - _sessionStateContainer.CuesheetChanged += SessionStateContainer_CuesheetChanged; - _sessionStateContainer.ImportCuesheetChanged += SessionStateContainer_ImportCuesheetChanged; - - _traceChangeManager.UndoDone += TraceChangeManager_UndoDone; - _traceChangeManager.RedoDone += TraceChangeManager_RedoDone; - - _sessionStateContainer.Cuesheet.TrackAdded += Cuesheet_TrackAdded; - _sessionStateContainer.Cuesheet.TrackRemoved += Cuesheet_TrackRemoved; - - AttachTracksToValidateablePropertyChanged(); - AttachCuesheetToSplitPointsAddedRemoved(); - } - - protected override async Task OnAfterRenderAsync(bool firstRender) - { - await base.OnAfterRenderAsync(firstRender); - _logger.LogDebug("OnAfterRenderAsync({firstRender})", firstRender); - if ((revalidate) && (validations != null)) - { - await validations.ValidateAll(); - revalidate = false; - } - } - - Task OnAddTrackClicked() - { - var newTrack = new Track(); - Cuesheet?.AddTrack(newTrack, applicationOptions); - _traceChangeManager.TraceChanges(newTrack); - return Task.CompletedTask; - } - - void OnDeleteTrackClicked(Track track) - { - Cuesheet?.RemoveTrack(track); - selectedTracks.Remove(track); - } - - async Task EditSelectedTracksClicked() - { - if (modalTrackEdit != null) - { - modalTrackEdit.TracksToEdit = selectedTracks; - await modalTrackEdit.Show(); - } - } - - private void DeleteSelectedTracksClicked() - { - Cuesheet?.RemoveTracks(selectedTracks.AsReadOnly()); - selectedTracks.Clear(); - } - - async Task OnDeleteAllTracksClicked() - { - _logger.LogInformation("OnDeleteAllTracksClicked"); - //Display a confirm warning - if (modalDialog != null) - { - modalDialog.Title = _localizer["Confirmation required"]; - modalDialog.Text = _localizer["Do you really want to delete all tracks?"]; - modalDialog.ModalSize = ModalSize.Small; - modalDialog.Mode = ModalDialog.DialogMode.Confirm; - void deleteTracksDelegate(object? sender, EventArgs args) - { - _logger.LogInformation("deleteTracksDelegate"); - Cuesheet?.RemoveTracks(Cuesheet.Tracks); - selectedTracks.Clear(); - modalDialog.Confirmed -= deleteTracksDelegate; - StateHasChanged(); - }; - modalDialog.Confirmed += deleteTracksDelegate; - await modalDialog.ShowModal(); - } - } - - async Task EditTrackModal(Track trackToEdit) - { - if (modalTrackEdit != null) - { - modalTrackEdit.TracksToEdit = new List() { trackToEdit }; - await modalTrackEdit.Show(); - } - } - - private String? GetLocalizedString(Boolean expressionToCheck, String localizedStringName) - { - if (expressionToCheck == true) - { - return _localizer[localizedStringName]; - } - else - { - return null; - } - } - - bool SelectAllIndeterminate - { - get => selectedTracks.Count > 0 && selectedTracks.Count < Cuesheet?.Tracks.Count; - } - - private void SelectedTrackChanged(Track track, bool selected) - { - if (selected) - { - selectedTracks.Add(track); - } - else - { - selectedTracks.Remove(track); - } - } - - private void OnSelectAllTracks(bool select) - { - if (select) - { - if (Cuesheet != null) - { - foreach (var track in Cuesheet.Tracks) - { - if (!selectedTracks.Contains(track)) - { - selectedTracks.Add(track); - } - } - } - } - else - { - selectedTracks.Clear(); - } - } - - bool AllTracksSelected - { - get => selectedTracks.Count > 0 && selectedTracks.Count == Cuesheet?.Tracks.Count; - } - - async Task CopyTrackClicked(Track trackToCopy) - { - var copy = new Track(trackToCopy); - Cuesheet?.AddTrack(copy, applicationOptions); - _traceChangeManager.TraceChanges(copy); - await EditTrackModal(copy); - } - - private async Task OnPlayTrackClicked(Track track) - { - if (AudioPlayer != null) - { - await AudioPlayer.OnPlayTrackClicked(track); - } - } - - private void LocalizationService_LocalizationChanged(object? sender, EventArgs args) - { - StateHasChanged(); - validations?.ValidateAll(); - } - - private MarkupString GetMarkupString(String? stringValue) - { - MarkupString result = new MarkupString(String.Empty); - if (stringValue != null) - { - result = new MarkupString(stringValue); - } - return result; - } - - private void SessionStateContainer_CuesheetChanged(object? sender, EventArgs args) - { - DetachTrackFromValidateablePropertyChanged(); - DetachCuesheetFromSplitPointsAddedRemoved(); - selectedTracks.Clear(); - StateHasChanged(); - AttachTracksToValidateablePropertyChanged(); - AttachCuesheetToSplitPointsAddedRemoved(); - } - - private void SessionStateContainer_ImportCuesheetChanged(object? sender, EventArgs args) - { - // Unsubscribe to previous attached events - DetachCuesheetFromSplitPointsAddedRemoved(); - var tracks = TracksAttachedToValidateablePropertyChanged.Except(_sessionStateContainer.Cuesheet.Tracks); - for (int i = tracks.Count() - 1; i >= 0; i--) - { - var track = tracks.ElementAt(i); - DetachTrackFromValidateablePropertyChanged(track); - TracksAttachedToValidateablePropertyChanged.Remove(track); - } - // Reattach if needed - AttachTracksToValidateablePropertyChanged(); - AttachCuesheetToSplitPointsAddedRemoved(); - revalidate = true; - StateHasChanged(); - } - - private void TraceChangeManager_UndoDone(object? sender, EventArgs args) - { - StateHasChanged(); - } - - private void TraceChangeManager_RedoDone(object? sender, EventArgs args) - { - StateHasChanged(); - } - - void Cuesheet_TrackAdded(object? sender, TrackAddRemoveEventArgs args) - { - StateHasChanged(); - AttachTracksToValidateablePropertyChanged(); - revalidate = true; - } - - void Cuesheet_TrackRemoved(object? sender, TrackAddRemoveEventArgs args) - { - DetachTrackFromValidateablePropertyChanged(args.Track); - revalidate = true; - } - - async Task OnReadDataAutocompleteTrackArtist(AutocompleteReadDataEventArgs autocompleteReadDataEventArgs) - { - if (!autocompleteReadDataEventArgs.CancellationToken.IsCancellationRequested) - { - var artists = await _musicBrainzDataProvider.SearchArtistAsync(autocompleteReadDataEventArgs.SearchValue); - if (!autocompleteReadDataEventArgs.CancellationToken.IsCancellationRequested) - { - autocompleteTrackArtists = artists; - } - } - } - - async Task OnReadDataAutocompleteTrackTitle(AutocompleteReadDataEventArgs autocompleteReadDataEventArgs, Track track) - { - if (!autocompleteReadDataEventArgs.CancellationToken.IsCancellationRequested) - { - var titles = await _musicBrainzDataProvider.SearchTitleAsync(autocompleteReadDataEventArgs.SearchValue, track.Artist); - if (!autocompleteReadDataEventArgs.CancellationToken.IsCancellationRequested) - { - autocompleteTrackTitles = titles; - } - } - } - - async Task OnSelectedValueChangedTrackTitle(Guid selectedValue, Track track) - { - switch (_sessionStateContainer.CurrentViewMode) - { - case ViewMode.ViewModeFull: - case ViewMode.ViewModeImport: - var trackDetails = await _musicBrainzDataProvider.GetDetailsAsync(selectedValue); - if (trackDetails != null) - { - track.Length = trackDetails.Length; - if (String.IsNullOrEmpty(track.Artist)) - { - track.Artist = trackDetails.Artist; - } - } - break; - } - } - - void LocalStorageOptionsProvider_OptionSaved(object? sender, IOptions options) - { - if (options is ApplicationOptions) - { - applicationOptions = (ApplicationOptions)options; - } - } - - void AttachTracksToValidateablePropertyChanged() - { - if (Cuesheet != null) - { - foreach (var track in Cuesheet.Tracks) - { - if (TracksAttachedToValidateablePropertyChanged.Contains(track) == false) - { - track.ValidateablePropertyChanged += Track_ValidateablePropertyChanged; - TracksAttachedToValidateablePropertyChanged.Add(track); - } - } - } - } - - void DetachTrackFromValidateablePropertyChanged(Track? track = null) - { - if (track == null) - { - foreach (var trackCurrentlyAttached in TracksAttachedToValidateablePropertyChanged) - { - trackCurrentlyAttached.ValidateablePropertyChanged -= Track_ValidateablePropertyChanged; - } - } - else - { - track.ValidateablePropertyChanged -= Track_ValidateablePropertyChanged; - } - } - - void Track_ValidateablePropertyChanged(object? sender, string property) - { - if (validations != null) - { - validations.ValidateAll().GetAwaiter().GetResult(); - } - StateHasChanged(); - } - - void AttachCuesheetToSplitPointsAddedRemoved() - { - if (Cuesheet != null) - { - Cuesheet.SectionAdded += Cuesheet_SectionAdded; - Cuesheet.SectionRemoved += Cuesheet_SectionRemoved; - } - } - - void DetachCuesheetFromSplitPointsAddedRemoved() - { - if (Cuesheet != null) - { - Cuesheet.SectionAdded -= Cuesheet_SectionAdded; - Cuesheet.SectionRemoved -= Cuesheet_SectionRemoved; - } - } - - void Cuesheet_SectionAdded(object? sender, CuesheetSectionAddRemoveEventArgs args) - { - args.Section.ValidateablePropertyChanged += Section_ValidateablePropertyChanged; - } - - void Cuesheet_SectionRemoved(object? sender, CuesheetSectionAddRemoveEventArgs args) - { - args.Section.ValidateablePropertyChanged -= Section_ValidateablePropertyChanged; - } - - void Section_ValidateablePropertyChanged(object? sender, string property) - { - switch (property) - { - case nameof(CuesheetSection.Begin): - case nameof(CuesheetSection.End): - StateHasChanged(); - break; - } - } -} diff --git a/AudioCuesheetEditor/Shared/TrackList/TrackList.razor b/AudioCuesheetEditor/Shared/TrackList/TrackList.razor new file mode 100644 index 00000000..1d027ad8 --- /dev/null +++ b/AudioCuesheetEditor/Shared/TrackList/TrackList.razor @@ -0,0 +1,489 @@ + + +@implements IDisposable + +@inject ITextLocalizer _localizer +@inject SessionStateContainer _sessionStateContainer +@inject ILocalStorageOptionsProvider _localStorageOptionsProvider +@inject TraceChangeManager _traceChangeManager +@inject ILogger _logger +@inject ITextLocalizerService _localizationService +@inject ITextLocalizer _validationMessageLocalizer + + + @if (_sessionStateContainer.CurrentViewMode == ViewMode.ViewModeFull) + { + var validationResult = Cuesheet?.Validate(x => x.Tracks); + + + @_localizer["Validation errors"] + + @if (validationResult?.ValidationMessages != null) + { + @foreach(var message in validationResult.ValidationMessages) + { + @message.GetMessageLocalized(_validationMessageLocalizer) + } + } + + + + + + + + + @if (TrackSelectionVisible == false) + { + + + + } + else + { + + + + } + + + + + + + + + + + + + +
+ } + + + + + @if ((_sessionStateContainer.CurrentViewMode == ViewMode.ViewModeFull) && (TrackSelectionVisible)) + { + +
+ @if (Cuesheet?.Tracks.Count > 0) + { + + + + } + @_localizer["Selection"] +
+
+ } + @switch (_sessionStateContainer.CurrentViewMode) + { + case ViewMode.ViewModeFull: + case ViewMode.ViewModeImport: + @_localizer["Controls"] + break; + case ViewMode.ViewModeRecord: + @_localizer["Controls"] + break; + } + # + @_localizer["Artist"] + @_localizer["Title"] + @_localizer["Begin"] + @_localizer["End"] + @_localizer["Length"] +
+
+ + + +
+
+ + + + +@code { + ModalDialog? modalDialog; + EditTrackModal? modalTrackEdit; + List selectedTracks = new(); + Boolean _trackSelectionVisible = false; + Validations? validations; + Boolean revalidate = false; + List TracksAttachedToValidateablePropertyChanged = new(); + DropContainer? dropContainer; + + [Parameter] + public AudioPlayer? AudioPlayer { get; set; } + + public Cuesheet? Cuesheet + { + get + { + Cuesheet? cuesheet; + switch (_sessionStateContainer.CurrentViewMode) + { + case ViewMode.ViewModeImport: + cuesheet = _sessionStateContainer.ImportCuesheet; + break; + default: + cuesheet = _sessionStateContainer.Cuesheet; + break; + } + return cuesheet; + } + } + + public Boolean TrackSelectionVisible + { + get => _trackSelectionVisible; + set + { + _trackSelectionVisible = value; + selectedTracks = new(); + } + } + + public void Dispose() + { + _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; + _sessionStateContainer.CuesheetChanged -= SessionStateContainer_CuesheetChanged; + _sessionStateContainer.ImportCuesheetChanged -= SessionStateContainer_ImportCuesheetChanged; + _traceChangeManager.UndoDone -= TraceChangeManager_UndoDone; + _traceChangeManager.RedoDone -= TraceChangeManager_RedoDone; + _sessionStateContainer.Cuesheet.TrackRemoved -= Cuesheet_TrackRemoved; + _sessionStateContainer.Cuesheet.TrackAdded -= Cuesheet_TrackAdded; + DetachTrackFromValidateablePropertyChanged(); + DetachCuesheetFromSplitPointsAddedRemoved(); + } + + protected override void OnInitialized() + { + base.OnInitialized(); + + _localizationService.LocalizationChanged += LocalizationService_LocalizationChanged; + + _sessionStateContainer.CuesheetChanged += SessionStateContainer_CuesheetChanged; + _sessionStateContainer.ImportCuesheetChanged += SessionStateContainer_ImportCuesheetChanged; + + _traceChangeManager.UndoDone += TraceChangeManager_UndoDone; + _traceChangeManager.RedoDone += TraceChangeManager_RedoDone; + + _sessionStateContainer.Cuesheet.TrackAdded += Cuesheet_TrackAdded; + _sessionStateContainer.Cuesheet.TrackRemoved += Cuesheet_TrackRemoved; + + AttachTracksToValidateablePropertyChanged(); + AttachCuesheetToSplitPointsAddedRemoved(); + } + + protected override async Task OnAfterRenderAsync(bool firstRender) + { + await base.OnAfterRenderAsync(firstRender); + _logger.LogDebug("OnAfterRenderAsync({firstRender})", firstRender); + if ((revalidate) && (validations != null)) + { + await validations.ValidateAll(); + revalidate = false; + } + } + + async Task OnAddTrackClicked() + { + var applicationOptions = await _localStorageOptionsProvider.GetOptions(); + var newTrack = new Track(); + Cuesheet?.AddTrack(newTrack, applicationOptions); + _traceChangeManager.TraceChanges(newTrack); + } + + async Task EditSelectedTracksClicked() + { + if (modalTrackEdit != null) + { + modalTrackEdit.TracksToEdit = selectedTracks; + await modalTrackEdit.Show(); + } + } + + private void DeleteSelectedTracksClicked() + { + Cuesheet?.RemoveTracks(selectedTracks.AsReadOnly()); + selectedTracks.Clear(); + } + + async Task OnDeleteAllTracksClicked() + { + _logger.LogInformation("OnDeleteAllTracksClicked"); + //Display a confirm warning + if (modalDialog != null) + { + modalDialog.Title = _localizer["Confirmation required"]; + modalDialog.Text = _localizer["Do you really want to delete all tracks?"]; + modalDialog.ModalSize = ModalSize.Small; + modalDialog.Mode = ModalDialog.DialogMode.Confirm; + void deleteTracksDelegate(object? sender, EventArgs args) + { + _logger.LogInformation("deleteTracksDelegate"); + Cuesheet?.RemoveTracks(Cuesheet.Tracks); + selectedTracks.Clear(); + modalDialog.Confirmed -= deleteTracksDelegate; + StateHasChanged(); + }; + modalDialog.Confirmed += deleteTracksDelegate; + await modalDialog.ShowModal(); + } + } + + private String? GetLocalizedString(Boolean expressionToCheck, String localizedStringName) + { + if (expressionToCheck == true) + { + return _localizer[localizedStringName]; + } + else + { + return null; + } + } + + bool SelectAllIndeterminate + { + get => selectedTracks.Count > 0 && selectedTracks.Count < Cuesheet?.Tracks.Count; + } + + private void OnSelectAllTracks(bool select) + { + if (select) + { + if (Cuesheet != null) + { + foreach (var track in Cuesheet.Tracks) + { + if (!selectedTracks.Contains(track)) + { + selectedTracks.Add(track); + } + } + } + } + else + { + selectedTracks.Clear(); + } + } + + bool AllTracksSelected + { + get => selectedTracks.Count > 0 && selectedTracks.Count == Cuesheet?.Tracks.Count; + } + + private void LocalizationService_LocalizationChanged(object? sender, EventArgs args) + { + StateHasChanged(); + validations?.ValidateAll(); + } + + private MarkupString GetMarkupString(String? stringValue) + { + MarkupString result = new MarkupString(String.Empty); + if (stringValue != null) + { + result = new MarkupString(stringValue); + } + return result; + } + + private void SessionStateContainer_CuesheetChanged(object? sender, EventArgs args) + { + DetachTrackFromValidateablePropertyChanged(); + DetachCuesheetFromSplitPointsAddedRemoved(); + selectedTracks.Clear(); + StateHasChanged(); + AttachTracksToValidateablePropertyChanged(); + AttachCuesheetToSplitPointsAddedRemoved(); + } + + private void SessionStateContainer_ImportCuesheetChanged(object? sender, EventArgs args) + { + // Unsubscribe to previous attached events + DetachCuesheetFromSplitPointsAddedRemoved(); + var tracks = TracksAttachedToValidateablePropertyChanged.Except(_sessionStateContainer.Cuesheet.Tracks); + for (int i = tracks.Count() - 1; i >= 0; i--) + { + var track = tracks.ElementAt(i); + DetachTrackFromValidateablePropertyChanged(track); + TracksAttachedToValidateablePropertyChanged.Remove(track); + } + // Reattach if needed + AttachTracksToValidateablePropertyChanged(); + AttachCuesheetToSplitPointsAddedRemoved(); + revalidate = true; + StateHasChanged(); + } + + private void TraceChangeManager_UndoDone(object? sender, EventArgs args) + { + StateHasChanged(); + } + + private void TraceChangeManager_RedoDone(object? sender, EventArgs args) + { + StateHasChanged(); + } + + void Cuesheet_TrackAdded(object? sender, TrackAddRemoveEventArgs args) + { + StateHasChanged(); + dropContainer?.Refresh(); + AttachTracksToValidateablePropertyChanged(); + revalidate = true; + } + + void Cuesheet_TrackRemoved(object? sender, TrackAddRemoveEventArgs args) + { + dropContainer?.Refresh(); + DetachTrackFromValidateablePropertyChanged(args.Track); + revalidate = true; + } + + void AttachTracksToValidateablePropertyChanged() + { + if (Cuesheet != null) + { + foreach (var track in Cuesheet.Tracks) + { + if (TracksAttachedToValidateablePropertyChanged.Contains(track) == false) + { + track.ValidateablePropertyChanged += Track_ValidateablePropertyChanged; + TracksAttachedToValidateablePropertyChanged.Add(track); + } + } + } + } + + void DetachTrackFromValidateablePropertyChanged(Track? track = null) + { + if (track == null) + { + foreach (var trackCurrentlyAttached in TracksAttachedToValidateablePropertyChanged) + { + trackCurrentlyAttached.ValidateablePropertyChanged -= Track_ValidateablePropertyChanged; + } + } + else + { + track.ValidateablePropertyChanged -= Track_ValidateablePropertyChanged; + } + } + + void Track_ValidateablePropertyChanged(object? sender, string property) + { + if (validations != null) + { + validations.ValidateAll().GetAwaiter().GetResult(); + } + StateHasChanged(); + } + + void AttachCuesheetToSplitPointsAddedRemoved() + { + if (Cuesheet != null) + { + Cuesheet.SectionAdded += Cuesheet_SectionAdded; + Cuesheet.SectionRemoved += Cuesheet_SectionRemoved; + } + } + + void DetachCuesheetFromSplitPointsAddedRemoved() + { + if (Cuesheet != null) + { + Cuesheet.SectionAdded -= Cuesheet_SectionAdded; + Cuesheet.SectionRemoved -= Cuesheet_SectionRemoved; + } + } + + void Cuesheet_SectionAdded(object? sender, CuesheetSectionAddRemoveEventArgs args) + { + args.Section.ValidateablePropertyChanged += Section_ValidateablePropertyChanged; + } + + void Cuesheet_SectionRemoved(object? sender, CuesheetSectionAddRemoveEventArgs args) + { + args.Section.ValidateablePropertyChanged -= Section_ValidateablePropertyChanged; + } + + void Section_ValidateablePropertyChanged(object? sender, string property) + { + switch (property) + { + case nameof(CuesheetSection.Begin): + case nameof(CuesheetSection.End): + StateHasChanged(); + break; + } + } +} diff --git a/AudioCuesheetEditor/Shared/TrackList/TrackListItem.razor b/AudioCuesheetEditor/Shared/TrackList/TrackListItem.razor new file mode 100644 index 00000000..24b19a35 --- /dev/null +++ b/AudioCuesheetEditor/Shared/TrackList/TrackListItem.razor @@ -0,0 +1,399 @@ + +@implements IDisposable + +@inject ITextLocalizer _localizer +@inject SessionStateContainer _sessionStateContainer +@inject ITextLocalizer _validationMessageLocalizer +@inject TraceChangeManager _traceChangeManager +@inject ApplicationOptionsTimeSpanParser _applicationOptionsTimeSpanParser +@inject MusicBrainzDataProvider _musicBrainzDataProvider +@inject ITextLocalizerService _localizationService + + + @if (Cuesheet != null) + { + + @if ((track != Cuesheet?.Tracks.FirstOrDefault()) && (_sessionStateContainer.CurrentViewMode == ViewMode.ViewModeFull)) + { + + + @if (track.IsLinkedToPreviousTrack) + { + + } + else + { + + } + + + } + + + @switch (_sessionStateContainer.CurrentViewMode) + { + case ViewMode.ViewModeRecord: + + + + @track.Position + break; + case ViewMode.ViewModeFull: + case ViewMode.ViewModeImport: + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + + + + + + + break; + } + + + + + @if (context.Item.Disambiguation != null) + { + @String.Format("{0} ({1})", context.Text, context.Item.Disambiguation) + } + else + { + @context.Text + } + + + + + + + + + @if (context.Item.Disambiguation != null) + { + @String.Format("{0} ({1})", context.Text, context.Item.Disambiguation) + } + else + { + @context.Text + } + + + + + @switch (_sessionStateContainer.CurrentViewMode) + { + case ViewMode.ViewModeRecord: + @track.Begin + @track.End + @track.Length + break; + case ViewMode.ViewModeFull: + case ViewMode.ViewModeImport: + +
+ @if (Cuesheet?.GetSectionAtTrack(track) != null) + { + + + + + + + + } + + + + + + + + + +
+
+ + + + + + + + + + + + + + + + + + + + + + + break; + } +
+
+ } +
+ + + +@code { + //TODO: More little components, like TrackLink, TrackSelection, etc. ;) + //TODO: Localization + Validations? validations; + EditTrackModal? modalTrackEdit; + IEnumerable? autocompleteTrackArtists; + IEnumerable? autocompleteTrackTitles; + + public void Dispose() + { + _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; + } + + [Parameter, EditorRequired] + public Boolean TrackSelectionVisible { get; set; } + + [Parameter, EditorRequired] + public IReadOnlyCollection SelectedTracks { get; set; } = new List(); + + [Parameter] + public EventCallback> SelectedTracksChanged { get; set; } + + [Parameter] + public AudioPlayer? AudioPlayer { get; set; } + + public Cuesheet? Cuesheet + { + get + { + Cuesheet? cuesheet; + switch (_sessionStateContainer.CurrentViewMode) + { + case ViewMode.ViewModeImport: + cuesheet = _sessionStateContainer.ImportCuesheet; + break; + default: + cuesheet = _sessionStateContainer.Cuesheet; + break; + } + return cuesheet; + } + } + + protected override void OnInitialized() + { + base.OnInitialized(); + _localizationService.LocalizationChanged += LocalizationService_LocalizationChanged; + } + + void LocalizationService_LocalizationChanged(object? sender, EventArgs args) + { + StateHasChanged(); + validations?.ValidateAll(); + } + + String? GetLocalizedString(Boolean expressionToCheck, String localizedStringName) + { + if (expressionToCheck == true) + { + return _localizer[localizedStringName]; + } + else + { + return null; + } + } + + async Task EditTrackModal(Track trackToEdit) + { + if (modalTrackEdit != null) + { + modalTrackEdit.TracksToEdit = new List() { trackToEdit }; + await modalTrackEdit.Show(); + } + } + + async Task CopyTrackClicked(Track trackToCopy) + { + var copy = new Track(trackToCopy); + //TODO + // Cuesheet?.AddTrack(copy, applicationOptions); + _traceChangeManager.TraceChanges(copy); + await EditTrackModal(copy); + } + + async Task OnPlayTrackClicked(Track track) + { + if (AudioPlayer != null) + { + await AudioPlayer.OnPlayTrackClicked(track); + } + } + + void SelectedTrackChanged(Track track, bool selected) + { + var selectedTracks = new List(SelectedTracks); + if (selected) + { + selectedTracks.Add(track); + } + else + { + selectedTracks.Remove(track); + } + SelectedTracksChanged.InvokeAsync(selectedTracks); + } + + void OnDeleteTrackClicked(Track track) + { + Cuesheet?.RemoveTrack(track); + var selectedTracks = new List(SelectedTracks); + selectedTracks.Remove(track); + SelectedTracksChanged.InvokeAsync(selectedTracks); + } + + async Task OnReadDataAutocompleteTrackArtist(AutocompleteReadDataEventArgs autocompleteReadDataEventArgs) + { + if (!autocompleteReadDataEventArgs.CancellationToken.IsCancellationRequested) + { + var artists = await _musicBrainzDataProvider.SearchArtistAsync(autocompleteReadDataEventArgs.SearchValue); + if (!autocompleteReadDataEventArgs.CancellationToken.IsCancellationRequested) + { + autocompleteTrackArtists = artists; + } + } + } + + async Task OnReadDataAutocompleteTrackTitle(AutocompleteReadDataEventArgs autocompleteReadDataEventArgs, Track track) + { + if (!autocompleteReadDataEventArgs.CancellationToken.IsCancellationRequested) + { + var titles = await _musicBrainzDataProvider.SearchTitleAsync(autocompleteReadDataEventArgs.SearchValue, track.Artist); + if (!autocompleteReadDataEventArgs.CancellationToken.IsCancellationRequested) + { + autocompleteTrackTitles = titles; + } + } + } + + async Task OnSelectedValueChangedTrackTitle(Guid selectedValue, Track track) + { + switch (_sessionStateContainer.CurrentViewMode) + { + case ViewMode.ViewModeFull: + case ViewMode.ViewModeImport: + var trackDetails = await _musicBrainzDataProvider.GetDetailsAsync(selectedValue); + if (trackDetails != null) + { + track.Length = trackDetails.Length; + if (String.IsNullOrEmpty(track.Artist)) + { + track.Artist = trackDetails.Artist; + } + } + break; + } + } +} diff --git a/AudioCuesheetEditor/Shared/TrackList/TrackSelection.razor b/AudioCuesheetEditor/Shared/TrackList/TrackSelection.razor new file mode 100644 index 00000000..329be82a --- /dev/null +++ b/AudioCuesheetEditor/Shared/TrackList/TrackSelection.razor @@ -0,0 +1,59 @@ + +@implements IDisposable + +@inject ITextLocalizer _localizer +@inject ITextLocalizerService _localizationService + +@if (Visible) +{ + + + + + +} + +@code { + //TODO: Localization + + [Parameter, EditorRequired] + public Boolean Visible { get; set; } + + [Parameter, EditorRequired] + public bool Selected { get; set; } + + [Parameter, EditorRequired] + public EventCallback SelectedChanged { get; set; } + + public void Dispose() + { + _localizationService.LocalizationChanged -= LocalizationService_LocalizationChanged; + } + + protected override void OnInitialized() + { + base.OnInitialized(); + _localizationService.LocalizationChanged += LocalizationService_LocalizationChanged; + } + + void LocalizationService_LocalizationChanged(object? sender, EventArgs args) + { + StateHasChanged(); + } +} diff --git a/AudioCuesheetEditor/_Imports.razor b/AudioCuesheetEditor/_Imports.razor index b86c652b..61538055 100644 --- a/AudioCuesheetEditor/_Imports.razor +++ b/AudioCuesheetEditor/_Imports.razor @@ -12,6 +12,7 @@ @using AudioCuesheetEditor @using AudioCuesheetEditor.Pages @using AudioCuesheetEditor.Shared +@using AudioCuesheetEditor.Shared.TrackList @using AudioCuesheetEditor.Model.AudioCuesheet @using AudioCuesheetEditor.Model.IO @using AudioCuesheetEditor.Model.IO.Export